美文网首页
实习 2.0

实习 2.0

作者: Tommmmm | 来源:发表于2018-09-17 14:29 被阅读0次

    Mybatis 自动Generate
    在Mybatis-Generate-XML里面,写相对应的表的信息,在Maven Project里Generate一下 就会自动生成

    select * from student limit 9,4
    语句2:slect * from student limit 4 offset 9
    // 语句1和2均返回表student的第10、11、12、13行

    1、动态数组:数组到底应该有多大才合适,有时可能不得而知。所以希望能够在运行时具有改变数组大小的能力。

    2、ArrayList
    线程不安全,Vector线程安全
    ArrayList是一个基于数组实现的链表(List)。
    从ArrayList中查找一个元素的index,其时间复杂度是o(n)
    ArrayList中根据index获取数组的时间复杂度是o(1)

     User user =new User(1,"Tom","123");
            List list = new ArrayList(initialCapacity:10);
            list.add(user);  //add与Get均为 Object
    

    每次尝试扩容到原来的1.5倍
    添加到指定index位置的时间复杂度是o(n),这里需要先把这个位置以及之后的元素统一向后移一位

    3、LinkList
    双向循环链表
    线程不安全

    private static class Node<E> {
            E item;
            Node<E> next;
            Node<E> prev;
    
            Node(Node<E> prev, E element, Node<E> next) {
                this.item = element;
                this.next = next;
                this.prev = prev;
            }
        }
    last/first/size等
    

    四、堆与栈

    ①堆:
    存储Java中的对象和数组
    队列优先,先进先出

    堆可以动态地分配内存大小,生存期也不必事先告诉编译器,因为它是在运行时动态分配内存的,但缺点是,由于要在运行时动态分配内存,存取速度较慢

    Q:new对象在堆中如何分配?
    A:由Java虚拟机的自动垃圾回收器来管理

    ②栈:
    栈内存主要是用来执行程序用的,比如:基本类型的变量和对象的引用变量。
    先进后出

    存取速度比堆要快,仅次于寄存器,栈数据可以共享。
    缺点是,存在栈中的数据大小与生存期必须是确定的,缺乏灵活性

    ③缓存
    栈使用的是一级缓存
    堆存放在二级缓存中

    缓存Cache:高速缓冲存储器
    缓存(Cache Memory)是位于CPU与内存之间的临时存储器,它的容量比内存小得多但是交换速度却比内存要快得多。缓存的出现主要是为了解决CPU运算速度与内存读写速度不匹配的矛盾。

    一级缓存:
    一级缓存都内置在CPU内部并与CPU同速运行,可以有效的提高CPU的运行效率。一级缓存越大,CPU的运行效率越高,但受到CPU内部结构的限制,一级缓存的容量都很小。

    二级缓存:
    位于CPU与内存之间的临时存储器

    ④寄存器Register
    寄存器是中央处理器CPU内的组成部份。寄存器是有限存贮容量的高速存贮部件,它们可用来暂存指令、数据和位址。
    寄存器拥有非常高的读写速度。
    寄存器的工作方式很简单,只有两步:
    (1)找到相关的位,(2)读取这些位。

    ⑤内存Memory:
    是用于存放数据的单元。其作用是暂时存放CPU中的运算数据,以及与硬盘等外部存储器交换的数据

    五、多线程

    线程阻塞与挂起:
    挂起join和睡眠是主动的,挂起恢复需要主动完成,睡眠恢复则是自动完成的。睡眠时间到则恢复到就绪态
    阻塞是被动的,是在等待某种事件或者资源的表现,一旦获得所需资源或者事件信息就自动回到就绪态。

    ①Runnable && Thread
    Runnable 是接口。里面只有Run()一个方法。
    Thread 是类,且实现了Runnable接口。且有Start()方法。

    在实际开发中以实现 Runnable 接口为主。
    线程池只能放入实现Runnable 类线程,不能直接放入继承Thread的类。

    public class Tickets implements Runnable {
        private Integer ticket=10;
        public void run(){
            for(int i=0; i<10 ;i++){
                System.out.println(Thread.currentThread().getName()+"-sell "+ticket--);
            }
        }
    }
    //main函数
            Tickets2 tickets = new Tickets2();
            Thread thread1 = new Thread(tickets,"windows1");
            Thread thread2 = new Thread(tickets,"windows2");
            Thread thread3 = new Thread(tickets,"windows3");
    
            thread1.start();
            thread2.start();
            thread3.start();
    

    三个线程共享一个tickets资源。

    ②Thread.join()
    在 Parent 调用 child.join() 后,child 子线程正常运行,Parent 父线程会等待 child 子线程结束后再继续运行。

    ③Synchronize
    当存在多个线程操作共享数据时,需要保证同一时刻有且只有一个线程在操作共享数据,其他线程必须等到该线程处理完数据后再进行。
    也就是说当一个共享数据被当前正在访问的线程加上互斥锁后,在同一个时刻,其他线程只能处于等待的状态,直到当前线程处理完毕释放该锁。

    public class Count2 implements Runnable{
    
        static int number = 0;
        public synchronized void increase(){
            number = number +1;
        }
        @Override
        public void run(){
            for(int i = 0 ; i < 100000 ; i++){
                increase();
            }
        }
    }
    //main函数
            Count2 count2 = new Count2();
            Thread thread = new Thread(count2,"thread1");
            Thread thread1 = new Thread(count2,"thread2");
            thread.start();
            thread1.start();
    
            while(true) {
                if (thread1.isAlive() == false && thread.isAlive() == false) {
                    System.out.println(Thread.currentThread().getName() + "--" + Count2.number);
                    break;
                }
            }
    

    如果不加Synchronize会显示结果main--134967,而不是20000;
    如果第二个线程在第一个线程读取旧值和写回新值期间读取number的值,那么第二个线程就会与第一个线程一起看到同一个值,并执行相同值的加1操作,这也就造成了线程安全失败。
    ⑥线程同步
    多线程加入同步锁以避免在该线程没有完成操作之前,被其他线程的调用。
    方法一:加入synchronized关键字来修饰方法
    同步代码块
    方法二:使用Volatile
    方法三:重入锁 ReentrantLock

    生产者消费者设计模式:生产者是一堆线程,消费者是另一堆线程,内存缓冲区为空的时候消费者必须等待,而内存缓冲区满的时候,生产者必须等待。在读写中只能存在一个线程

    6、mybatis与hibernate的区别
    Hibernate与MyBatis都是通过SessionFactoryBuider由XML配置文件生成SessionFactory,由SessionFactory 生成Session,由Session来开启执行事务和SQL。
    hibernate:是一个标准的ORM框架(对象关系映射)。不需要写sql,sql语句自动生成,对sql语句进行优化、修改比较困难。

    mybatis:专注sql本身,需要程序员自己编写sql语句,sql修改、优化比较方便。
    mybatis可以通过XML或注解方式灵活配置要运行的sql语句,并将java对象和sql语句映射生成最终执行的sql,最后将sql执行的结果再映射生成java对象。

    7、HashMap源码:线程不安全 not synchronized


    HashMap里面实现一个静态内部类Entry,其重要的属性有 key , value, next。

    Q:既然是线性数组,为什么能随机存取?
    A:将key的值进行hash之后进行存储。

    解决hash冲突的方法:
    开放地址法&&链地址法
    链表法是将相同 hash 值的对象组成一个链表放在 hash 值对应的槽位;

    补充:
    equals()方法:通过判断两个对象的地址是否相等来判断是不是同一个对象

    如有需求,需要重写方法

     @Override
         public boolean equals(Object obj){ }
    

    == : 它的作用是判断两个对象的地址是不是相等。即判断两个对象是不是同一个对象。

    hashCode() 的作用是获取哈希码,它实际上是返回一个int整数。这个哈希码的作用是确定该对象在哈希表中的索引位置。

    hashCode() 和 equals() 的关系:哈希值相同不一定equal。

    Q:为什么String, Interger这样的wrapper类适合作为键?
    A:因为String是不可变的,也是final的,而且已经重写了equals()和hashCode()方法了。

    见补充中的11*

    8、volatile
    Java 内存模型中的可见性
    一个线程修改的状态对另一个线程是可见的
    Java 中 volatile、synchronized 和 final 实现可见性

    volatile不具有原子性:非原子操作都会存在线程安全问题,需要我们使用同步技术(sychronized)来让它变成一个原子操作。

    a、在访问volatile变量时不会执行加锁操作,因此也就不会使执行线程阻塞,因此volatile变量是一种比sychronized关键字更轻量级的同步机制。

    CPU为了提高处理性能,并不直接和内存进行通信,而是将内存的数据读取到内部缓存再进行操作,但操作完并不能确定何时写回到内存。
    b、而声明变量是 volatile 的,JVM 保证了每次读变量都从内存中读,跳过 CPU cache 这一步。使用volatile关键字会强制将修改的值立即写入主存。

    c、保证此变量对所有的线程的可见性,这里的“可见性”,当一个线程修改了这个变量的值,volatile 保证了新值能立即同步到主内存,以及每次使用前立即从主内存刷新。

    9、JAVA中的内存泄露
    内存泄漏是指无用对象(不再使用的对象)持续占有内存或无用对象的内存得不到及时释放,从而造成内存空间的浪费称为内存泄漏。

    内存溢出是指内存不够用
    10、

    补充:

    1、接口与抽象类的异同:
    接口和抽象类都不能被实例化
    接口和抽象类都可以包含抽象方法,实现接口或继承抽象类的普通子类都必须实现这些抽象方法。

    接口里只能包含抽象方法,静态方法和默认方法,不能为普通方法提供方法实现,抽象类则完全可以包含普通方法
    接口里只能定义静态常量,不能定义普通成员变量,抽象类里则既可以定义普通成员变量,也可以定义静态常量
    接口不能包含构造器,抽象类可以包含构造器。
    接口里不能包含初始化块,但抽象类里完全可以包含初始化块。

    2、对JAVA IO的理解
    数据不停地移入移出缓冲区

    3、并发与并行
    并发:当有多个线程在操作时,如果系统只有一个CPU,它只能把CPU运行时间划分成若干个时间段,再将时间段分配给各个线程执行,在一个时间段的线程代码运行时,其它线程处于挂起状。

    并行:当系统有一个以上CPU时,则线程的操作有可能非并发。当一个CPU执行一个线程时,另一个CPU可以执行另一个线程,两个线程互不抢占CPU资源,可以同时进行

    4、Runtime
    Runtime 类代表着Java程序的运行时环境,每个Java程序都有一个Runtime实例,该类会被自动创建,我们可以通过Runtime.getRuntime() 方法来获取当前程序的Runtime实例。

    5、


    程序计数器是一块较小的内存空间,它的作用可以看做是当前线程所执行的字节码的行号指示器。
    由于Java 虚拟机的多线程是通过线程轮流切换并分配处理器执行时间的方式来实现的,在任何一个确定的时刻,一个处理器只会执行一条线程中的指令。
    因此,为了线程切换后能恢复到正确的执行位置,每条线程都需要有一个独立的程序计数器。

    方法区:是各个线程共享的内存区域,它用于存储已被虚拟机加载的类信息、常量、静态变量、即时编译器编译后的代码等数据。

    6、同步接口与异步接口
    同步交互:指发送一个请求,需要等待返回,然后才能够发送下一个请求,有个等待过程;
    异步交互:指发送一个请求,不需要等待返回,随时可以再发送下一个请求,即不需要等待。

    同步与异步
    同步:多线程同时操作一个数据,要保证该共享数据在同一时刻只被一个线程使用。因此需要等待资源访问结束。效率低,但是安全性好

    异步:等待某事件的过程中继续做自己的事,不需要等待这一事件完成后再工作。异步操作可以避免线程阻塞。

    阻塞与非阻塞:
    阻塞和非阻塞关注的是程序在等待调用结果时的状态

    阻塞调用是指调用结果返回之前,当前线程会被挂起。调用线程只有在得到结果之后才会返回。
    非阻塞调用指在不能立刻得到结果之前,该调用不会阻塞当前线程。

    死锁:
    A,B,C 三个线程都拿着一个资源同时请求另一个由其它人占用的资源,就是像我们平时说的一手交钱,一手交货,但双方都不放手的过程。

    7、重写与重载
    方法的重载和重写(Override、覆盖)都是实现多态的方式。重载发生在一个类中,同名的方法如果有不同的参数列表;重写发生在子类与父类之间,重写要求子类被重写方法与父类被重写方法有相同的参数列表(返回类型不一定相同)
    OOP三大特性:继承,多态,封装。

    8、是否可以继承String类?
    String 类是final类,不可以被继承。

    final类
    a当用final修饰一个类时,表明这个类不能被继承。

    9、聚集索引与非聚集索引
    都采用B+树结构

    聚集索引就类似新华字典中的拼音排序索引,都是按顺序进行,例如找到字典中的“爱”,就里面顺序执行找到“癌”。而非聚集索引则类似于笔画排序,索引顺序和物理顺序并不是按顺序存放的。

    聚集索引和非聚集索引的根本区别是表记录的排列顺序和与索引的排列顺序是否一致。

    索引的优点:
    1.大大加快数据的检索速度;
    2.创建唯一性索引,保证数据库表中每一行数据的唯一性;
    3.加速表和表之间的连接;

    索引的缺点:
    1.索引需要占物理空间。
    2.当对表中的数据进行增加、删除和修改的时候,索引也要动态的维护,降低了数据的维护速度。

    10、SpringMVC的流程

    1、 用户发送请求至前端控制器DispatcherServlet。
    2、 DispatcherServlet收到请求调用HandlerMapping处理器映射器。
    4、 DispatcherServlet调用HandlerAdapter处理器适配器
    5、HandlerAdapter经过适配调用具体的处理器(Controller,也叫后端控制器)。
    6、 Controller执行完成返回ModelAndView。
    7、 HandlerAdapter将controller执行结果ModelAndView返回给DispatcherServlet。

    8、 DispatcherServlet将ModelAndView传给ViewReslover视图解析器。
    9、ViewReslover解析后返回具体View。

    关键词:DispatcheServlet 映射器 处理器 controller ModelAndView DispatcheServlet 视图解析器 view

    11、HashTable 与ConcurrentHashMap
    HashMap 实际上是一个“链表散列”的数据结构,即数组和链表的结合体。

    HashTable在 Java 4 中被重写了,实现了 Map 接口,所以也成了 Java 集合框架中的一部分

    HashMap 几乎可以等价于 HashTable,除了 HashMap 是非 synchronized 的,并可以接受 null(HashMap 可以接受为 null 的键值 (key) 和值 (value),而 HashTable 则不行)

    HashMap是一种无序的存储结构,根据HashCode来存放。

    sychronized 意味着在一次仅有一个线程能够更改 Hashtable。就是说任何线程要更新 Hashtable 时要首先获得同步锁,其它线程要等到同步锁被释放之后才能再次获得同步锁更新 Hashtable。

    Q:我们能否让 HashMap 同步?
    HashMap 可以通过下面的语句进行同步:

    Map m = Collections.synchronizeMap(hashMap);
    

    ConcurrentHashMap:
    ConcurrentHashMap同步性能比HashTable更好。

    HashTable容器使用synchronized来保证线程安全,但在线程竞争激烈的情况下HashTable的效率非常低下。因为当一个线程访问HashTable的同步方法时,其他线程访问HashTable的同步方法时,可能会进入阻塞状态。如线程1使用put进行添加元素,线程2不但不能使用put方法添加元素,并且也不能使用get方法来获取元素

    ConcurrentHashMap锁分段:首先将数据分成一段一段的存储segment,然后给每一段数据配一把锁,当一个线程占用锁访问其中一个段数据的时候,其他段的数据也能被其他线程访问。

    一个Segment里包含一个HashEntry数组,每个HashEntry是一个链表结构的元素。当对HashEntry数组的数据进行修改时,必须首先获得它对应的Segment锁。

    12、扩容方式:
    ArrayList:
    ArrayList底层采用Object类型的数组实现。
    向ArrayList添加对象时,原对象数目加1如果大于原底层数组长度,则以适当长度1.5倍新建一个原数组的拷贝,并修改原数组,指向这个新数组。原数组自动抛弃(java垃圾回收机制会自动回收)

    HashMap:
    Hashmap的扩容需要满足两个条件:
    当前数据存储的数量(即size())大小必须大于等于阈值;
    当前加入的数据是否发生了hash冲突。

    transfer()方法将原有Entry数组的元素拷贝到新的Entry数组里。思想同ArrayList。

    13、drop delete
    DELETE语句执行删除的过程是每次从表中删除一行
    Delete from 数据表名称 where 列名称=值;

    Drop :删除数据表或数据库,或删除数据表字段。
    rop database 数据库名称

    Truncate:删除数据表中的数据(仅数据表中的数据,不删除表)。
    truncate table 数据表名称

    14、AOP的实现原理
    采用动态代理技术,利用截取消息的方式,对该消息进行装饰,以取代原有对象行为的执行

    15、JAVA的内存分配
    1.寄存器:最快的存储区, 由编译器根据需求进行分配,我们在程序中无法控制.

    1. 栈:存放基本类型的变量数据和对象的引用,但对象本身不存放在栈中,而是存放在堆(new 出来的对象)或者常量池中(字符串常量对象存放在常量池中。)
    2. 堆:存放所有new出来的对象。
    3. 静态域:存放静态成员(static定义的)
    4. 常量池:存放字符串常量和基本类型常量(public static final)。

    16、线程间共享数据:
    a、线程执行的任务相同:如卖票

    ShareData task = new ShareData(); //一个类实现了Runnable接口
    
           for(int i = 0; i < 4; i ++) {   //四个线程来卖票       
               new Thread(task).start();
           }
    
    class ShareData implements Runnable {
    
        private int data = 100;
        @Override
        public void run() { //卖票,每次一个线程进来,先判断票数是否大于0
    //      while(data > 0) {
                synchronized(this) {
                    if(data > 0) {
                        System.out.println(Thread.currentThread().getName() + ": " + data);
                        data--;
                    }
                }
    //      }
        }
    

    注意:给当前任务TASK线程加锁。

    17、数据库索引类型
    普通索引:没有任何限制
    CREATE INDEX indexName ON mytable(username(length));
    唯一索引:MySQL数据库索引列的值必须唯一,但允许有空值
    CREATE UNIQUE INDEX indexName ON mytable(username(length))
    主键索引:不允许有空值
    组合索引:

    18、Runnable和Thread比较
    Thread自己就实现了Runnable
    Thread实际上是对Runnable的包装
    实际上最终被线程执行的任务是Runnable,而非Thread

     Runnable runnable = new Runnable() {
                @Override
                public void run() {
             
                }
            }
    

    19、Mysql对索引对创建方式
    a.由数据库的查询优化器自动判断是否使用索引:
    数据库在收到查询语句后会查看where语句后面的查询条件,同时查看在表上面有哪些索引,然后根据查询条件和索引进行匹配
    b.强制使用索引
    select * from table force index(ziduan1_index) limit 2;
    (强制使用索引"ziduan1_index")

    20、JVM的内存模型
    a、程序计数器:
    标示下一条需要执行的指令的位置
    不同的线程之间需要不停的获得运行,挂起等待运行,所以各线程之间的计数器互不影响,独立存储。
    b、虚拟机栈
    有人将java内存区域划分为栈与堆两部分,在这种粗略的划分下,栈标示的就是当前讲的虚拟机栈。这种划分方法关心的是新申请内存的存在空间。
    c、本地方法栈
    d、JAVA堆
    此块内存的唯一目的就是存放对象实例,几乎所有的对象实例都在对上分配内存。
    e、方法区
    用于存储被虚拟机加载的类信息、常量、静态变量、即时编译的代码
    f、常量池:
    各种字面量和符号引用

    21、Integer的Equals ==
    Integer a= new Integer(5);
    Integer b= new Integer(5);
    Integer c=127;
    Integer d = 127;
    Integer e = 128;
    Integer f = 128;
    int g = 128;
    System.out.println(a==b); FALSE
    System.out.println(a.equals(b)); TRUE
    System.out.println(c == d); TRUE
    System.out.println(e == f); FLASE
    System.out.println(f == g);TRUE

    22、HashMap的Put原理


    实现了Map的接口
    允许为null , hashtable不允许为null

    首先判断table,也就是数组是否为空,为空的话就去使用inflateTable的方法初始化hashmap。

    如果table不为空的话,就判断key是否为空,为空的话就将放到数组的index=0的位置,如果value不为空则返回value值。

    如果key不为空的话,就通过key获取hash值,通过hash值和table的长度与运算获取hashCode值。
    int hash = hash(key);
    int i = indexFor(hash, table.length);

    通过hashCode的遍历entry<K,V>的键值对,如果key的hash值相等 并且key.equals(e.key)也相等的话,就将新的value替换掉旧的,返回旧值。

    相关文章

      网友评论

          本文标题:实习 2.0

          本文链接:https://www.haomeiwen.com/subject/coaowftx.html