1

Java基础

自己临时整合存储

面向对象的特征?

  • 封装:将对象不需要让外界访问的成员变量和方法私有化,只提供公有方法访问。
  • 继承:从父类继承信息创建子类。(子类继承父类所有属性和方法,但是私有属性和方法子类是无法访问的,只是拥有。子类可以拥有自己的属性和方法,子类可以以自己的方式实现父类的方法(重写))
  • 多态:子类继承父类并重写父类的方法,父类引用指向子类实例,也可以用接口实现。

多态分为几种?

  • 编译时多态:方法的重载。
  • 运行时多态:父类引用指向的类型在运行期间才确定。要满足三个条件,继承关系,重写父类方法,父类引用指向子类实例。

多态的好处和弊端

  • 多态的好处:提高了代码的维护性(继承保证),提高了代码的扩展性(由多态保证)。
  • 多态的弊端:不能使用子类的特有属性和行为。(不能使用子类特有的成员属性和成员方法。)

多态成员的访问特点

  • 成员变量:编译看左边(父类),运行看左边(父类)。
  • 成员方法:编译看左边(父类),运行看右边(子类)。动态绑定
  • 静态方法:编译看左边(父类),运行看左边(父类)。(静态和类相关,算不上重写,所以,访问还是左边的)

Java类能多继承吗?

  • java类只能单继承(多重继承会导致冲突,加大复杂性。)
  • 接口可以多继承

JAVA数据类型

  • 整型数据类型
类型 默认值(缺省值) 字节 范围
byte 0 1 8 -128 ~ 127
short 0 2 16 -32,768 ~ 32,767
int 0 4 32 -2,147,483,648 ~ 2,147,483,647
long 0L 8 64 -2⁶³ ~ 2⁶³ -1
  • 浮点型数据类型
类型 默认值(缺省值) 字节 范围
float 0.0f 4 32 -3.4E-038 ~ 3.4E+038
double 0.0d 8 64 -1.7E-308 ~ 1.7E+308
  • 字符和布尔数据类型
类型 默认值(缺省值) 字节 范围
boolean false 1 1 true,false
char 'u0000'(空,不会打印出的) 2 16

Java访问权限修饰符

名称 当前类 本包 本包子类 不同包子类 任何地方
private × × × ×
默认(default) × ×
protected ×
public

Java变量

  • 成员变量:在方法、构造方法和语句块之外。

    • 实例变量(对象变量):对象实例化后,每个实例变量就被初始化,只能被对象调用。

      + 存在于堆内存中,随着对象被回收而消失。
    • 静态变量(类变量):static修饰的成员变量,属于类本身,可以用类名和对象调用

      + 存在于方法区中,随着类的消失而消失。
  • 局部变量(本地变量):方法中,方法的形式参数,与方法共存亡。(存在于栈中)

java是值传递的

基本数据类型传递的是值的copy,引用类型传递的是地址值的copy。
  • 基本类型的传递:方法只是得到了拥有相同值的新变量,只能修改新变量的值,不影响原先的变量。
  • 引用类型的传递:方法得到了拥有相同地址值的新对象,只能修改对象的值。
  • 引用类型的传递:不能让原先的对象引用新的地址值。

构造方法有哪些特性?

  • 名字与类相同。
  • 没有返回值,但也不能用void声明构造函数。
  • 生成类的对象时自动执行,无需调用。

构造器Constructor是否可以被override?

  • 父类私有属性和构造方法并不能被继承,所以Constructor也就不能被override(重写),但是可以overload(重载),所以你可以看到一个类中有多个构造函数的情况。

一个类的构造方法作用是什么?若没有声明构造方法,程序能执行吗?

  • 主要作用是完成对类的初始化工作。
  • 可以执行,因为一个类即使没有声明构造方法也会有默认的无参构造方法。

java中的无参构造方法的作用?

  • 对该类成员进行初始化操作。
  • 在程序执行子类构造方法之前,如果没用super()来调用父类特定的构造方法,则会调用父类无参构造方法。

调用子类构造方法前会先调用父类构造方法,目的是什么?

  • 对继承自父类的成员初始化操作。

初始化顺序

存在继承的情况下,初始化顺序为:

  • 父类(静态变量、静态语句块)
  • 子类(静态变量、静态语句块)
  • 父类(实例变量、普通语句块)
  • 父类(构造函数)
  • 子类(实例变量、普通语句块)
  • 子类(构造函数)

重载和重写的区别?

  • 重载:发生在同一个类中,方法名必须相同,参数类型不同,个数不同,顺序不同,方法返回值和访问修饰符可以不同,发生在编译时。
  • 重写:发生在父子类中,方法名,参数列表必须相同,返回值范围小于等于父类,访问修饰符范围大于等于父类;如果父类方法访问修饰符为private则子类就不能重写该方法。

有了基础类型为什么需要包装类型?

java是面向对象的语言,而基本类型不具备面向对象特征。
    • 包装类型可以为null,而基本类型不可以。
    • 包装类型可以用于泛型,而基本类型不可以。
    • 基本类型比包装类型更高效:

      • 基本类型在栈中直接存储具体数值。
      • 包装类型在栈中存储的是堆中的引用。
    • 包装类型的值可以相同,但却不相等。

    什么是自动拆箱和自动装箱?

    • 装箱:将基本类型用它们对应的引用类型包装起来。(包装类型引用指向基本类型)
    • 拆箱:将包装类型转换为基本数据类型。(基本类型引用指向包装类型)

    接口和抽象类的区别是什么?

    • 接口的默认方法是public,所有方法在接口中不能有实现(java8开始接口方法可以有默认实现),而抽象类可以有非抽象方法。
    • 接口中除了static,final变量,不能有其他变量,而抽象类不一定。
    • 一个类可以实现多个接口,但只能实现一个抽象类。接口自己本身可以通过继承(extends)拓展多个接口。
    • 接口方法默认修饰符是public,而抽象方法可以有public,protected和default这些修饰符(抽象方法就是为了被重写所有不能使用private关键字修饰)
    • 从设计层面来说,抽象是对类的抽象,是一种模板设计,而接口是对行为的抽象,是一种行为规范。
    在jdk1.8中,接口也可以定义为静态方法,可以直接用接口名调用。实现类和实现是不可以调用的。如果同时实现两个接口,接口中定义了一样的默认方法,则必须重写,不然会报错。

    成员变量与局部变量的区别有哪些?

    • 成员变量属于类,局部变量是方法的参数和变量。成员变量可以用public,private,static等修饰符修饰,局部变量则不能,但他们都能被final修饰。
    • 从变量在内存中的存储方式来看,static修饰的成员变量是属于类的。如果没有static修饰,那这个变量是属于实例的。对象存在于堆内存,局部变量则存在于栈内存。
    • 成员变量是对象的一部分,随着对象的创建而创建,而局部变量随着方法调用而自动消失。
    • 成员变量变量如果没有被赋初值;则会以类型的默认值而赋值。(被final修饰的成员变量也必须显式的赋值),而局部变量则不会自动赋值。

    对象的相等和引用的相等,两者有什么不同?

    • 对象的相等比较的是内存中的内容是否相等。而引用相等,比较的是他们指向的内存地址是否相等。

    静态方法和实例方法有何不同?

    • 调用静态方法无需创建对象,可以直接用类名和对象调用。
    • 静态方法访问本类的成员时,只允许访问静态成员(静态成员变量和静态方法),而不允许访问实例成员变量和实例方法;实例方法则无此限制。

    静态方法里调用非静态成员为什么是非法的?

    • 由于静态方法可以不通过对象进行调用,因此在静态方法里,不能调用其他非静态变量,也不可以访问非静态变量成员。

    什么是方法返回值,返回值在类的方法里的作用是什么?

    • 方法的返回值是指我们获取到的某个方法体中的代码执行结果!
    • 返回值的作用:接收出结果,使它可以用于其他的操作!

    创建一个对象用什么运算符?对象实体与对象引用有何不同?

    • new运算符,new创建对象实例(对象实例在堆内存中),对象引用指向对象实例(对象引用放在栈内存中)。
    一个对象引用可以指向0个或1个对象(一根绳子可以不系气球,也可以系一个气球);一个对象可以有n个引用指向它(可以用n条绳子系住一个气球)

    关于final关键字的一些总计

    final关键字主要用在三个地方:变量、方法、类。
    
    • 被final修饰变量,基本数据一旦初始化不能更改、引用类型初始化后不能指向另一个对象。
    • 被final修饰的类,不能被继承,所以的成员方法都被隐式的指定为final方法。
    • 使用final的原因:

      • 第一个原因是把方法锁定,以防止任何继承类修改它的含义;
      • 第二个原因是效率。在早期的 java实现版本中,会将final方法转为内嵌调用。但是如果方法过于庞大,可能看不到内嵌调用带来的任何性能提升(现在的java版本已经不需要使用final方法进行这些优化了)类中所有的private方法都隐式的制定为final。

    super关键字总结

    • 访问父类的构造函数:可以使用 super() 函数访问父类的构造函数,从而委托父类完成一些初始化的工作。
    • 访问父类的成员:如果子类重写了父类的中某个方法的实现,可以通过使用 super 关键字来引用父类的方法实现。

    this关键字

    • this关键字用来表示当前对象本身。(this.成员)
    • this表示当前类的实例(创建实例X x = new X(); 调用:x.成员)

    final、finally、finalize的区别

    • final是修饰符,可以修饰类,变量,方法。
    • finally 是用于异常处理的场面,无论是否有异常抛出,都会执行。
    • finalize是Object的方法,所有类都继承了该方法。 当一个对象满足垃圾回收的条件,并且被回收的时候,其finalize()方法就会被调用。

    String为什么是不可变的?

    String使用final关键字修饰字符数组来保存字符串,所以是不可变的。

    • 好处:

      • 常量是线程安全的。
      • 不可变的特性作为hashMap的key使得hash值也不可变,只需要计算一次。
      • StringPool常量池的需要,重复引用。
    • 坏处:

      • 修改String值会重新创建新的常量或对象,而不是修改原有的值。

    String的两种创建方法的区别

    String A = "123" 和 String B = new String("123")

    • 1.引用直接指向字符串,栈内存中地址值直接指向了常量池。 只创建了一个对象
    • 2.new实例,如果常量池中没有,会创建两个对象,栈内存地址值指向堆内存中的对象实例,再指向常量池中的字符串。

    StringBuilder 和 StringBuffer 的区别是什么?

    • 相同:

      • 都是可变的。
      • 初识容量都是16个字符,当该对象的实体存放的字符长度大于16时,实体容量就自动增加。
      • 尝试将新容量扩为大小变成原容量的1倍+2,然后if判断一下 容量如果不够,直接扩充到需要的容量大小。
    • 不同:

      • StringBuffer是线程安全的,内部使用 synchronized 进行同步。
      • StringBuilder是线程不安全的。

    == 和 equals(重要)

    • == 基本类型比较的是值,引用类型比较的是地址值。
    • equals();它的作用也是判断两个对象是否相等。
    • 但它一般有两种使用情况:

      • 类没有覆盖equals方法,则equals等价于==
      • 覆盖equals方法,比较对象时,若他们内容相等,则返回true
    String中的equals方法是被重写过的,因为object方法的equals方法比较的对象内存地址,而string得equals方法比较得是对象得值
    (当创建 String 类型的对象时,虚拟机会在常量池中查找有没有已经存在的值和要创建的值相同的对象,如果有就把它赋给当前引用。如果没有就在常量池中重新创建一个 String 对象。)

    hashCode() 的作用

    • 获取哈希码,也称散列码;它实际上返回的是一个int整数。这个哈希码的作用是确定对象在该哈希表中的索引位置。
    散列表指的是java集合中本质是散列表的类hashMap,hashTable,hashSet

    hashCode() 在散列表中才有用,在其它情况下没用。

    在散列表中hashCode() 的作用是获取对象的散列码,进而确定该对象在散列表中的位置。

    hashCode与equals(重要)

    • 如果两个对象相等,则hashCode一定也是相同的。
    • 两个对象相等,对两个对象分别调用equals方法都返回true。
    • 两个对象有相同的hashCode值,它们也不一定是相等的。
    • 因此,equals 方法被覆盖过,则 hashCode 方法也必须被覆盖。
    • hashCode() 的默认行为是对堆上的对象产生独特值。
    • 如果没有重写hashCode(),则该 class 的两个对象无论如何都不会相等(即使这两个对象指向相同的数据)

    什么是java序列化,如何实现java序列化?

    • 序列化指的是把一个Java对象,通过某种介质进行传输,比如Socket输入输出流,或者保存在一个文件里。
    • 实现java序列化的手段是让该类实现接口 Serializable,这个接口是一个标识性接口,没有任何方法,仅仅用于表示该类可以序列化。

    浅克隆和深克隆有什么区别?

    区别主要在对引用类型的复制上,具体信息如下:

    • 浅克隆:只会复制对象的值类型,而不会复制对象的引用类型;
    • 深克隆:复制整个对象,包含值类型和引用类型。

    如何实现浅克隆?

    • 克隆的对象实现 Cloneable 接口,并重写 clone() 方法就可以实现浅克隆了。

    深克隆如何实现?有几种实现方式?

    一般实现方式有以下两种:

    • 通过序列化实现深克隆(序列化实现方式:Java 原生序列化、JSON 序列化、Hessian 序列化);
    • 所有引用类型都实现克隆(Cloneable 接口,并重写 clone() 方法),从而实现深克隆。

    Error和Exception有什么区别?

    Error和Exception都实现了Throwable接口

    • Error指的是JVM层面的错误,比如内存不足OutOfMemoryError
    • Exception指的是代码逻辑的异常,比如下标越界OutOfIndexException

    Java 中 IO 流分为几种?

    • 按照数据的流向:输入流、输出流
    • 按照流数据的格式:字符流、字节流
    • 按照流数据的包装过程:节点流(低级流)、处理流(高级流)

    InputStream/Reader: 所有的输入流的基类,前者是字节输入流,后者是字符输入流。
    OutputStream/Writer: 所有输出流的基类,前者是字节输出流,后者是字符输出流。

    BIO,NIO,AIO 有什么区别?

    • BIO:Block IO 同步阻塞式 IO,就是我们平常使用的传统 IO,它的特点是模式简单使用方便,并发处理能力低。
    • NIO:Non IO 同步非阻塞 IO,是传统 IO 的升级,客户端和服务器端通过 Channel(通道)通讯,实现了多路复用。
    • AIO:Asynchronous IO 是 NIO 的升级,也叫 NIO2,实现了异步非堵塞 IO ,异步 IO 的操作基于事件和回调机制。

      BIO是一个连接一个线程。
      NIO是一个请求一个线程。
      AIO是一个有效请求一个线程。

    • BIO:同步并阻塞,服务器实现模式为一个连接一个线程,即客户端有连接请求时服务器端就需要启动一个线程进行处理,如果这个连接不做任何事情会造成不必要的线程开销,当然可以通过线程池机制改善。
    • NIO:同步非阻塞,服务器实现模式为一个请求一个线程,即客户端发送的连接请求都会注册到多路复用器上,多路复用器轮询到连接有I/O请求时才启动一个线程进行处理。
    • AIO:异步非阻塞,服务器实现模式为一个有效请求一个线程,客户端的I/O请求都是由OS先完成了再通知服务器应用去启动线程进行处理。

    适用场景分析:

    • BIO方式适用于连接数目比较小且固定的架构,这种方式对服务器资源要求比较高,并发局限于应用中,JDK1.4以前的唯一选择,但程序直观简单易理解。
    • NIO方式适用于连接数目多且连接比较短(轻操作)的架构,比如聊天服务器,并发局限于应用中,编程比较复杂,JDK1.4开始支持。
    • AIO方式使用于连接数目多且连接比较长(重操作)的架构,比如相册服务器,充分调用OS参与并发操作,编程比较复杂,JDK7开始支持。

    我又何存
    4 声望1 粉丝