JAVASE基础复习大全

JAVA面向对象基础

1、说一下什么是面向对象
面向对象是一种编程思想,它是相对于面向过程而言的,从执行者变为了指挥者,通过这种思想来将生活中复杂的事情简单化

面向对象就是把一个对象抽象成类,具体来说就是把一个对象的静态特征和动态特征抽象成属性和方法,也就是把一类事物的算法和数据结构封装在一个类中,程序就是多个对象之间相互通信组成的

面向对象三大特征 1、封装 2、继承 3、多态

封装

封装是指隐藏对象的属性和实现的细节,仅仅对外提供公共的访问方式,提高了安全性。

继承

提高复用性,只要继承父类,就能拥有父类的功能

多态

1、多态是指同一个实体同时具有多种形式,可以把不同的子类对象都当做父类来看,可以屏蔽不同子类对象之间的差异,写出通用的代码,做出通用的编程,统一的调用标准。

2、提高了程序的扩展性和可维护性

多态的三大必要条件:

1)继承        2)重写        3)父类引用指向子类对象  向上转型

2、说一下类和对象

1)Java语言最基本的单位就是类

2)类是一类事物的抽象

对象

对象具有三大特点:对象的状态、对象的行为、对象的标识

1)对象的状态是用来描述对象的基本特征

2)对象的行为用来描述对象的功能

3)对象的标识是指对象在内种中都有一个唯一的地址

类和对象的区别

类:如手机类,抽取相同的行为和属性 行为作为成员方法 属性作为成员变量

对象:可以按照类模板生产出多个手机,1号手机对象、2号手机对象

属性:颜色、尺寸、品牌、价格

行为:打电话,发短信

对象在内存中的存储

1)局部变量存在栈中,方法执行完毕内存就被释放

2)对象(new出来的东西)存在堆中,对象不再使用时,内存才会被释放

3)每个堆内存中的元素都有地址值

4)对象中的属性都是有默认值的

3、说一下构造方法

1)构造方法的方法名必须与类名相同

2)构造方法没有返回值类型 也不能定义为void  (定义为void后则是一个普通方法)       

3)可以有多个构造方法,如果没有定义系统会创建一个无参默认构造方法

4、构造代码块、局部代码块

构造代码块

1)构造代码块在类中方法外

2)用来提取构造方法中通用代码,每次调用构造方法时都会先执行构造代码块

3)优先于构造方法加载

局部代码块

1)在方法中的代码块

2)通常用于控制变量的作用范围,出了括号就失效

5、this关键字

1)构造方法中,this()必须放在第一行(子类构造方法第一行默认调用父类super()关键字,所以调用this()则必须放在第一行,否则则会出现super() 其他输出语句 this() 此时则调用了2次super() 会报错)

6、super关键字

1)如果用,必须出现在调用位置的第一行 (因为子类继承了父类的属性和方法,所以在先初始化父类的属性和方法,这样子类才可以初始化自己特有的,因为java中不允许调用没有初始化的成员)

7、this和super的区别

1)this代表本类对象的引用,super代表父类对象的引用

2)this用于区分成员变量和局部变量

3)super用于区分本类变量和父类变量

4)this.成员变量  this.成员方法()  this(【参数】),代表调用本类内容

5)super.成员变量  super.成员方法()  super(【参数】),代表调用父类内容

6)this和super不可以同时出现在同一个构造方法中,它们两个只要出现必须在第一行

8、访问权限修饰

                       本类          同包         子类        任意

public                √                √              √             √

protected          √                √              √             

default              √                √                          

private                

9、方法重写

规则:子类重写父类方法(方法名、参数列表、返回值)完全一致才算重写

1)父类中的私有方法不能被重写

2)子类重写父类方法时,访问修饰符要大于等于父类

10、重载和重写的区别

重载:同一个类中的多个方法具有相同的名字,但它们的参数列表不同,即参数的数量,类型,顺序不同

重写:是存在于父子类之间的,子类定义的方法与父类中的方法具有相同的名字,相同的的参数列表和返回值类型(子类的访问修饰符要大于等于父类的)

11、static

1)可以修饰成员变量和成员方法

2)随着类的加载而加载,优先于对象加载

3)只加载一次,就会一直存在,不再开辟新空间

4)可以直接被类名调用

5)静态只能调用静态,非静态可以随意调用

6)static不能和this或者super共用,因为有static时可能还没有对象

7)全局唯一,全局共享

12、final

1)final修饰的变量是个常量,值不能被更改

2)final修饰的方法不能被重写

3)final修饰的类不能被继承

13、异常

Java中异常主要分为两类

Error:系统错误,无法修复

Exception:可修复的错误

Exception中常见异常类型:

1)RuntimeException 

2)ClassCastException

3)ClassNotFoundException

异常的两种处理方式:

        捕获或向上抛出

14、静态代码块

执行顺序:静态代码块→构造代码块→局部代码块

1)在类加载时就加载,并且只被加载一次,一般用于项目的初始化

15、静态变量和实例变量的区别

实例变量属于某个对象的属性,必须创建了实例对象,其中的实例变量才会被分配空间,才能使用这个实例变量。

静态变量不属于某个实例对象,而是属于类,所以也被称为类变量,只要程序加载了类的字节码,不用创建任何实例对象,静态变量就会被分配空间,静态变量就可以被使用

1)静态变量需要用static来修饰

2)实例变量需要创建对象后才可以通过这个对象来使用

3)静态变量可以直接使用类名来引用

16、抽象类

Java中没有方法体的方法,该方法由其子类来具体的实现。该没有方法题的方法我们称之为抽象方法,含有抽象方法的类我们称为抽象类

2)抽象类中可以没有抽象方法,如果类中有抽象方法,那该类必须定义为一个抽象类

3)子类继承了抽象类以后,要么也是一个抽象类,要么就把所有的抽象方法都重写

4)抽象类类不能被实例化

抽象类中:

1)既可以有变量,也可以有常量

2)既可以有普通方法,也可以有抽象方法

抽象方法:

1)抽象方法要被实现,所以不能是静态的,也不能是私有的

2)构造方法:抽象类中也有构造方法,但是本身无法被实例化

问题:

1)抽象类的构造方法有什么用?

一般用于给子类实例化,当创建子类对象调用子类构造方法时会先自动调用父类构造方法

2、abstract可以和static一起使用吗

用static声明方法表明这个方法在不生成类的实例时可直接被类调用,而抽象方法不能被调用,两者矛盾,所以,不能

17、接口

Java中由于不允许多重继承,所以如果要实现多个类的功能,则可以通过实现多个接口来实现

提高程序的复用率,增加程序的可维护性,可扩展性,就必须是面向接口的编程,面向抽象的编程

特点:

1)接口中都是抽象方法

2)通过interface关键字创建接口,通过implements让子类来实现

3)接口之间可以多实现,接口和接口之间可以多继承

构造方法:

1)接口中没有构造方法

2)在创建实现类的对象时默认的super(),是调用的Object的无参构造

重点:

1)接口中没有成员变量,都是常量,所以,接口中的变量没有写修饰符时都会默认加上

      public static final

2)接口里的方法,默认就是抽象的,如果你不写abstract,则会默认补齐

18、Java设计模式

Java中共有23种设计模式

单例模式:

常见的Spring默认创建的bean就是单例模式的

其中单例模式最重要的是确保对象只有一个(简单来说,保证一个类在内存中的对象就一个)

1)饿汉式

        1)私有化构造方法

        2)在类的内部创建好对象

        3)对外界提供一个公共的get(),返回一个已经准备好的对象

public class Test{

		private Test(){ }
		
		private static Test t = new Test();

		public static Test get(){
		
			return t;

		}
		
	}

2)懒汉式

        1)私有化构造方法

        2)在类的内部创建好一个对象,不赋值

        3)提供一个公共的get(),在中判断对象是否已有地址值,没有则对象进行赋值,并返回

public class Test{

		private Test(){ }
		
		private static Test t = null;
 
		public static Test get(){
			if(t == null){

				t= new Test();
			}
		
			return t;
		}
		
	}

19、抽象类和接口的区别

1)抽象类中有普通方法,接口中没有

2)抽象类中可以有普通变量,接口中只能是公共的静态常量

3)接口可以继承接口,并可多继承接口,但类只能单继承

4)抽象类中有构造方法,接口中没有

20、StringBuffer和StringBuilder

StringBuffer 线程安全

StringBuilder 非线程安全

内部维护 char[ ]  value ,默认初始化数组容量为16

1)StringBuffer和StringBuilder有一定的缓冲区,当字符串没有超过容量时,不会分类新的容量,当字符串大小超过容量时,会自动增加容量

2)StringBuilder速度更快,线程不安全,StringBuffer线程安全

与String的区别是:

1)StringBuffer和StringBuilder的对象能够被多次的修改,并且不产生新的对象,String每次操作都会产生新的对象

2)由于 StringBuilder 相较于 StringBuffer 有速度优势,多数情况下建议使用StringBuilder。如果应用程序要求线程安全的话则必须使用StringBuffer

21、IO流

流:是指一串连续的数据(字符串或节),是以先进先出的方式发送信息的通道

1)先进先出:最先写入输出流的数据最先被输出流读取到

2)顺序存取:可以一个接一个地往流中写入一串字节,读出时也将按写入顺序读取一串字节,不能随机访问中间的数据。

3)只读或只写:每个流只能是输入流或输出流的一种,不能同时具备两个功能,输入流只能进行读操作,对输出流只能进行写操作。在一个数据传输通道中,如果既要写入数据,又要读取数据,则要分别提供两个流。

IO流分类:

1)按数据流的方向:输入流、输出流

2)按处理数据单位:

        字节流:处理一切 

        字符流:处理纯文本

3)按功能:节点流、处理流

节点流和处理流:

1)节点流:直接操作数据读写的流类,比如FileInputStream FileOutputStrean FileReader FileWriter 

2)处理流:对一个已存在的流的链接和封装,通过对数据进行处理为程序提供功能强大、灵活的读写功能,例如BufferedInputStream(缓冲字节流)

22、BIO、AIO、NIO的区别

同步:发起一个调用后,被调用者未处理完请求,调用不返回

异步:发起一个调用后,被调用者立即回应已经接收到请求,但是被调用者并没有返回结果,此时我们可以去处理其他的请求,被调用者通常通过事件、回调机制来通知调用者返回其结果

阻塞:发起一个请求后,调用者一直等待请求结果返回,无法干其他事情

非阻塞:发起一个请求后,调用者不用一直等待结果返回,可以先干其他事情

BIO同步阻塞I/O模式:

        用户进程在发起一个IO操作以后,必须等待IO操作的完成,只有当真正完成了IO操作以后,用户进程才能运行

NIO同步非阻塞I/O模式:    

        用户进程发起一个IO操作以后边可返回做其它事情,但是用户进程需要时不时的询问IO操作是否就绪,这就要求用户进程不停的去询问,从而引入不必要的CPU资源浪费

AIO异步非阻塞I/O模式:

        用户进程只需要发起一个IO操作然后立即返回,等IO操作真正的完成以后,应用程序会得到IO操作完成的通知,此时用户进程只需要对数据进行处理就好了,不需要进行实际的IO读写操作,因为真正的IO读取或者写入操作已经由内核完成了

23、Collection

Collection父接口常用的两个子接口 
    List子接口:数据有序,可以重复
         -- ArrayList子类
         -- LinkedList子类
         -- Vector子类
    Set子接口:不可以重复
         -- HashSet子类
         -- TreeSet子类

Collection常用方法
    boolean add(E e):添加元素。

    boolean addAll(Collection  c):把小集合添加到大集合中 。

    boolean contains(Object o) : 如果此 collection 包含指定的元素,则返回 true。

    boolean isEmpty() :如果此 collection 没有元素,则返回 true。

    Iterator<E> iterator():返回在此 collection 的元素上进行迭代的迭代器。

    boolean remove(Object o) :从此 collection 中移除指定元素的单个实例。

    int size() :返回此 collection 中的元素数。

    Objec[] toArray():返回对象数组

24、List

ArrayList

通过数组实现,查询快,增删慢,支持随机访问,线程不安全,初始容量是10(jdk1.8以前是10,jdk1.8以后为0,新增数据才扩容为10),每次扩容到当前容量的1.5倍

LinkedList

1)通过双向链表实现,查询慢(移动指针),增删快,线程不安全

2)链表维护了一个size(链表长度)、Node<E> first(链表头),Node<E> last(链表尾),Node的结构中有个item、next、prev充分说明了此链表是一个双向链表

  //链表的长度
    transient int size = 0;
 
    /**
     * Pointer to first node.
     * Invariant: (first == null && last == null) ||
     *            (first.prev == null && first.item != null)
     */
    //链表的头
    transient Node<E> first;
 
    /**
     * Pointer to last node.
     * Invariant: (first == null && last == null) ||
     *            (last.next == null && last.item != null)
     */
    //链表的尾
    transient Node<E> last;
    //链表维护的Node的结构
    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;
        }
}

 Vector:

        底层数组实现,查询快,增删慢,由于线程安全,所以效率低,扩容为当前2倍

        子类:stack 栈

                  特点:先进先出,对Vector进行了扩展

25、ArrayList和LinkedList的区别

1)底层数据结构不同:ArrayList底层是数组 LinkedList底层是双向链表

2)ArrayList支持随机访问而LinkedList不支持

        ArrayList源码中它实现了一个RandomAccess接口,然而这个接口是个空接口,实现这个空接口的意义就是标记这个类具有一个随机访问的特性。

       --最根本原因是ArrayList是根据数组下标去访问里面的数据

3)插入和删除的效率不同

        ArrayList:是采用数组实现的,插入和删除受到元素位置的影响,会产生数据迁移

        LinkedList:采用的是双向链表存储数据,所以不会收到元素位置影响,直接改变指针位置即可,所以比较快

4)  ArrayList:执行频繁查找效率比LinkedList高

        LinkedList:执行频繁的插入和删除效率比ArrayList高

25、HashSet和TreeSet

HashSet:通过HashMap实现,无序,数据不可重复,可以存储一个null,线程不安全

TreeSet:通过TreeMap实现,自然排序,数据不可重复,可存储一个null,线程不安全

26、Map

HashMap:

        HashMap是一个无序的K,V结构容器,jdk1.8以前为数组+链表,以后为数组+链表+红黑树

初始化容量为16,默认的加载因子为0.75,每次扩容为当前容量的一倍,当链表长度大于8且数组

长度大于64时会转换为红黑树,线程不安全

TreeMap:

        底层通过红黑树实现,自然排序,线程不安全

HashTable:

        底层为哈希表,主要是线程安全,键和值都不允许为null,扩容机制为原来的2n+1

27、ConcurrentHashMap

28、多线程

线程的4种创建方式:

        1、继承Thread类

        2、实现Runnable接口

        3、实现Callable接口

        4、创建线程池

线程的6种状态:新建状态→可运行状态→阻塞状态→无限等待状态→超时等待状态→终止状态

下面详细介绍了线程的6种状态,如按照5种状态理解则将无限等待状态+超时等待状态+阻塞状态理合为阻塞状态,将可运行状态分为就绪状态运行中状态)如下:

线程的5种状态:新建状态→就绪状态→运行状态→阻塞状态→终止状态

新建状态:new 了一个线程对象,但是还没有调用start()方法时

可运行状态可运行状态包含 就绪状态 和 运行状态 

                      就绪状态:当调用了线程对象的start()方法,等待被线程调度选中,获取CPU的使

                                        用权

                        运行状态:就绪状态的线程获得CPU时间片之后变成运行中状态

阻塞状态:

        当一个线程试图获取一个对象锁,而该对象锁被其他的线程持有,则该线程进入Blocked状态;当该线程持有锁时,该线程将变成Runnable状态

        1)线程对象调用sleep时,当前线程进入阻塞状态

        2)运行在当前线程里的其它线程调用join()方法,当前线程进入阻塞状态

        3)等待用户输入的时候,当前线程进入阻塞状态

无限等待状态:

        一个线程在等待另一个线程执行一个(唤醒)动作时,该线程进入Waiting状态。进入这个状态后是不能自动唤醒的,必须等待另一个线程调用notify或者notifyAll方法才能够唤醒

超时等待状态

        同waiting状态,有几个方法有超时参数,调用他们将进入Timed Waiting状态,这一状态将一直保持到超时期满或者接收到唤醒通知,带有超时参数的常用方法有Thread.sleep、锁对象.wait()

终止状态:

       因为run方法正常退出而死亡,或者因为没有捕获的异常终止了run方法而死亡

线程中常用的方法及概念

wait():Object中的方法,必须在Synchronized修饰的方法或块中使用,调用A.wait()的线程将加入到该对象A的等待池中,此时该线程进入阻塞状态,并且释放掉持有的当前对象A的锁

sleep():Thread中的静态方法,可以在任何地方使用,让当前正在执行的线程暂停一段时间,让出cpu资源,进入等待状态,不会释放锁,休眠时间结束以后进入可运行状态

yield():Thread中的方法,作用是将执行该方法的线程对象回到可运行状态(放弃当前拥有的cpu资源),给优先级相同或更高(如没有相同优先级或更高的则不生效)的其他线程来获取运行机会,实际结果中不能够保证一定会调用其他线程,调用yield()的线程有可能再次被调度程序选中

join():Thread中的方法,会使主线程(或调用t.join()的线程)进入等待状态,等该线程执行完毕后才被唤醒,进入可运行状态,并不影响同一时刻处于运行中状态的其他线程

PS:join()方法的底层是利用wait()方法实现的,但是join()源码中只调用了wait()方法,并没有调用notify()方法,是因为线程在终止的时候会自动调用自身的notifyAll()方法,来释放所有的资源和锁

notify():Object中的方法,当使用某个对象的notify()方法时,将从该对象的等待池中选择一个等待的线程唤醒,唤醒的线程将进入锁池争夺对象锁

PS:使用了notify方法进行唤醒,而notify方法只能唤醒一个线程,其它等待的线程仍然处于wait状态,假设调用 increment 方法的线程执行完后,所有的线程都处于等待状态,此时 又执行了notify方法,这时如果唤醒的是一个increment方法的调度线程,那么while循环等于true,则此唤醒的线程也会处于等待状态,此时所有的线程都处于等待状态,那么也就没有了运行的线程来唤醒它们,这就发生了死锁。

如果使用notifyAll方法来唤醒所有正在等待该锁的线程,那么所有的线程都会处于运行前的准备状态(就是increment方法执行完后,唤醒了所有等待该锁的状态,注:不是wait状态),那么此时,即使再次唤醒一个increment方法调度线程,while循环等于true,唤醒的线程再次处于等待状态,那么还会有其它的线程可以获得锁,进入运行状态

总结:notify方法很容易引起死锁,除非你根据自己的程序设计,确定不会发生死锁,notifyAll方法则是线程的安全唤醒方法

notifyAll():Object中的方法,当使用某个对象的notify()方法时,将该对象的等待池中所有等待的线程唤醒,唤醒的线程将进入锁池争夺对象锁

锁池:假设线程A已经拥有了某个对象(注意:不是类)的锁,而其它的线程想要调用这个对象的某个synchronized方法(或者synchronized代码块),由于这些线程在进入对象的synchronized方法之前必须先获得该对象的锁的拥有权,但是该对象的锁目前正被线程A拥有,所以这些线程就进入了该对象的锁池中,等待争夺锁的拥有权

等待池/等待队列:假设一个线程A调用了某个对象的wait()方法,线程A就会释放该对象的锁(因为wait()方法必须出现在synchronized中,这样自然在执行wait()方法之前线程A就已经拥有了该对象的锁),同时线程A就进入到了该对象的等待池中。如果另外的一个线程调用了相同对象的notifyAll()方法,那么处于该对象的等待池中的线程就会全部进入该对象的锁池中,准备争夺锁的拥有权。如果另外的一个线程调用了相同对象的notify()方法,那么仅仅有一个处于该对象的等待池中的线程(随机)会进入该对象的锁池

29、Synchronized

        在静态方法上加锁:锁的是当前Class对象

        在普通方法上加锁:锁的是当前实例对象

        在代码块上加锁:括号中指定的对象

Synchronized的原理有2个

  • 内置锁
  • 互斥锁

内置锁:每个java对象都可以用做一个实现同步的锁,这些锁称为内置锁(Intrinsic Lock)或者监视器锁(Monitor Lock)。线程进入同步代码块或方法的时候会自动获得该锁,并且在退出同步代码块时(正常返回,或者是异常退出)会自动释放锁。获得内置锁的唯一途径就是进入这个锁的保护的同步代码块或方法

互斥锁:而Java的内置锁又是一个互斥锁,这就是意味着最多只有一个线程能够获得该锁,当线程B尝试去获得线程A持有的内置锁时,线程B必须 等待(WAITING)或者阻塞(BLOCKED),直到线程A释放这个锁,如果A线程不释放这个锁,那么B线程将永远等待下去

30、反射

        反射就是在运行状态中,对于任何一个类都可以知道他的属性和方法,对于任何一个对象都可以调用它的属性和方法

反射对象的三种创建方式:

        1、类.class

        2、对象.getClass()

        3、Class.forName(全路径名)

框架

1、spring springMVC springboot

spring springMVC springboot_豪大大ya的博客-CSDN博客

  • 2
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值