巨人的肩膀JAVA面试总结

Posted 生命是有光的

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了巨人的肩膀JAVA面试总结相关的知识,希望对你有一定的参考价值。

💪

目录

1、Java和C++的区别

Java 和 C++ 都是面向对象的语言,都支持封装、继承和多态,但是,它们还是有挺多不相同的地方:

  • Java 不提供指针来直接访问内存,程序内存更加安全
  • Java 的类是单继承的,C++ 支持多重继承;虽然 Java 的类不可以多继承,但是接口可以多继承。
  • Java 有自动内存管理垃圾回收机制(GC),不需要程序员手动释放无用内存。
  • C ++同时支持方法重载和操作符重载,但是 Java 只支持方法重载

2、标识符和关键字的区别是什么

  • 标识符就是一个名字。我们在写代码时,会为类、变量、方法取名字,这个名字就是标识符
  • 有一些标识符Java语言已经赋予了其特殊的含义,这些特殊的标识符就是关键字

3、continue、break和return的区别是什么

  • continue: 指跳出当前的这一次循环,继续下一次循环
  • break :指跳出整个循环体,继续执行循环下面的语句
  • return :用于跳出所在方法,结束该方法的运行

4、成员变量和局部变量的区别

  • 成员变量:作用范围是整个类,相当于C中的全局变量,定义在方法体和代码块之外,一般定义在类的声明之下;成员变量包括实例成员变量和静态成员变量(类变量);

  • 局部变量:类的方法中的变量,访问修饰符不能用于局部变量,声明在方法、构造方法或代码块中,在上分配,无默认值,必须初始化后才能使用;

  • 成员变量是属于对象的,可以被 public,private,static 等修饰符所修饰。局部变量是在代码块或者在方法中定义的变量或者是方法的参数,局部变量不能被 public,private,static 等修饰。但是成员变量和局部变量都能被 final 关键字所修饰

  • 若成员变量使用 static 修饰,那么这个成员变量属于类。如果没有用 static 修饰,那么这个成员变量是属于实例的。实例(对象)存在于堆内存,局部变量则存在于栈内存

  • 成员变量属于对象,随着对象的创建而存在。而局部变量随着方法的调用而自动生成,随着方法的调用结束而消亡。

  • 成员变量如果没有被赋初始值,则会自动以类型的默认值而赋值。但是如果被 final 关键字修饰必须显式的赋值,而局部变量不会自动赋值。

5、静态变量有什么用

  • 静态变量可以被类的所有实例共享。无论一个类创建了多少个对象,它们都共享同一份静态变量。

    • 静态变量:static 修饰
    • 静态常量:static final 修饰
  • 通常情况下,静态变量会被 final 关键字修饰成为常量

final 修饰 StringBuffer 后 还可以 append 吗?

可以。final 修饰的是一个引用变量,那么这个引用始终只能指向这个对象,但是这个对象内部的属性是可以变化的。

6、字符型常量和字符串常量的区别

  • 形式:字符常量是单引号引起的一个字符,字符串常量是双引号引起的 0 个或若干个字符。
  • 含义:字符常量相当于一个整型值( ASCII 值),可以参加表达式运算,字符串常量代表一个地址值(该字符串在内存中存放位置)。
  • 占内存大小:字符常量只占 2 个字节; 字符串常量占若干个字节。(char 在 Java 中占两个字节)

7、类中都可以包含哪些成分

  1. 成员变量:包括实例成员变量、静态成员变量
  2. 成员方法:包括实例方法,静态方法,抽象方法,getter setter 方法
  3. 构造器(构造函数):
    • 默认无参构造器:一个类默认会自带一个无参构造器,即使不写它也存在,但是如果一个类它写了一个构造器,那么默认的无参构造器就被覆盖了!
    • 有参构造器
  4. 代码块
  5. 内部类

8、static关键字

按照有无static修饰,成员变量和方法可以分为

  1. 成员变量
    1. 静态成员变量(类变量):有static修饰的成员变量称为静态成员变量,也叫类变量,属于类本身,直接用类名.静态成员变量访问即可
    2. 实例成员变量:无static修饰的成员变量称为实例成员变量,属于类的每个对象的,必须用对象.实例成员变量来访问
  2. 成员方法
    1. 静态方法:有static修饰的成员方法称为静态方法,也叫类方法,属于类本身的,直接用==类名.静态方法()==即可
    2. 实例方法:无static修饰的成员方法称为实例方法,属于类的每个对象的,必须用==对象.实例方法()==来访问

注意:静态方法和私有方法不可以被重写

  public static String staticField = "静态变量";
  static     
    System.out.println("静态代码块");   ---- 1
  
  public String field = "实例变量";       ---- 2

  
    System.out.println("实例代码块");  ----3
  
  // 最后才是构造函数的初始化
  public InitialOrderTest() 
    System.out.println("构造函数");    ----4
  

初始化顺序:静态变量和静态代码块优先于实例变量和实例代码块,静态变量和静态代码块的初始化顺序取决于它们在代码中的顺序

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

  1. 父类(静态变量、静态语句块)
  2. 子类(静态变量、静态语句块)
  3. 父类(实例变量、普通语句块)
  4. 父类(构造函数)
  5. 子类(实例变量、普通语句块)
  6. 子类(构造函数)

9、super关键字

  1. 访问父类的构造函数:可以使用 super() 函数访问父类的构造函数,从而委托父类完成一些初始化的工作。
  2. 访问父类的成员:如果子类重写了父类的某个方法,可以通过使用 super 关键字来引用父类的方法实现
  3. this 和 super 不能同时出现在一个构造函数里面,因为 this 必然会调用其它的构造函数,其它的构造函数必然也会有 super 语句的存在,所以在同一个构造函数里面有相同的语句,就失去了语句的意义,编译器也不会通过。

10、Java继承的特点

  1. 单继承: 一个类只能继承一个直接父类
  2. 多层继承:一个类可以间接继承多个父类(C继承B,B继承A)
  3. 一个类可以有多个子类(C继承B,D也继承B,B就有了C,D两个子类)
  4. 一个类要么默认继承了Object类,要么间接继承了Object类,Object类是Java中的祖宗类

为什么继承后子类的构造器会调用父类的构造器?

  • 子类的构造器第一行默认有super()调用父类的无参构造器,写不写都存在

11、Java中有几种基本数据类型

Java 中有 8 种基本数据类型,分别为:

  • 6种数字类型:
    • 4种整数型:byte、short、int、long
    • 2种浮点型:float、double
  • 1种字符类型:char
  • 1种布尔型:boolean

这 8 种基本数据类型的默认值以及所占空间的大小如下:

基本类型位数字节默认值
byte810
short1620
int3240
long6480
char162
float3240.0
double6480.0
boolean1false
  1. Java 里使用 long 类型的数据一定要在数值后面加上 L,否则将作为整型解析。
  2. char a = ‘h’ ,char 是单引号, String a = “hello” String 是双引号

12、基本类型和包装类型的区别

  • 成员变量包装类型不赋值就是 null ,而基本类型有默认值且不是 null
  • 包装类型可用于泛型,而基本类型不可以
  • 基本类型比包装类型更高效。基本类型在中直接存储的具体数值,而包装类型则存储的是中的引用。 很显然,相比较于基本类型而言,包装类型需要占用更多的内存空间。

int 和 Integer 有什么区别?

  • Integer是int的包装类;int是基本数据类型
  • Integer变量必须实例化后才能使用;int变量不需要
  • Integer实际是对象的引用,指向此new的Integer对象;int是直接存储数据值
  • Integer的默认值是null;int的默认值是0

两个 new 生成的 Integer 变量的对比:

由于Integer变量实际上是对一个Integer对象的引用,所以两个通过new生成的Integer变量永远是不相等的(因为new生成的是两个对象,其内存地址不同)

Integer i = new Integer(10000);
Integer j = new Integer(10000);
System.out.print(i == j); //false

13、包装类型的缓存机制了解吗

Java 基本数据类型的包装类型的大部分都用到了缓存机制来提升性能。

Byte,Short,Integer,Long 这 4 种包装类默认创建了数值 [-128,127] 的相应类型的缓存数据,Character 创建了数值在 [0,127] 范围的缓存数据,Boolean 直接返回 True or False

如果超出对应范围仍然会去创建新的对象,缓存的范围区间的大小只是在性能和资源之间的权衡。两种浮点数类型的包装类 Float,Double 并没有实现缓存机制。

Integer i1 = 33;
Integer i2 = 33;
System.out.println(i1 == i2);// 输出 true

Float i11 = 333f;
Float i22 = 333f;
System.out.println(i11 == i22);// 输出 false

Double i3 = 1.2;
Double i4 = 1.2;
System.out.println(i3 == i4);// 输出 false

14、什么是自动装箱与自动拆箱

  • 装箱:将基本类型用它们对应的引用类型包装起来。装箱就是调用了包装类的 ValueOf() 方法,
  • 拆箱:将包装类型转换为基本数据类型,拆箱就是调用了xxxValue() 方法。
Integer i = 10;  //装箱
// 等价于 Integer i = Integer.valueOf(10)
int n = i;   //拆箱
// 等价于int n = i.intValue()



Integer i1 = 40;
// Integer i1=40 这一行代码会发生装箱,也就是说这行代码等价于Integer i1=Integer.valueOf(40),因此 i1 直接使用的是缓存中的对象
Integer i2 = new Integer(40);
// Integer i2 = new Integer(40)会直接创建新的对象
System.out.println(i1==i2); // false

所有整型包装类对象之间值的比较,全部使用 equals 方法比较

  • 对于 Integer var = ? 在 -128至127之间的赋值,Integer 对象是在 IntegerCache.cache 产生,会复用已有对象,这个区间内的 Integer 值可以直接使用 == 进行判断,但是这个区间外的所有数据,推荐使用 equals 方法进行判断。

15、如何解决浮点数运算的时候精度丢失

首先为什么会出现浮点数运算的时候精度丢失问题呢?这个和计算机保存浮点数的机制有很大关系。我们知道计算机是二进制的,而且计算机在表示一个数字时,宽度是有限的,无限循环的小数存储在计算机时,只能被截断,所以就会导致小数精度发生损失的情况。这也就是解释了为什么浮点数没有办法用二进制精确表示。

如何解决呢?BigDecimal 可以实现对浮点数的运算,不会造成精度丢失。通常情况下,大部分需要浮点数精确运算结果的业务场景(比如涉及到钱的场景)都是通过 BigDecimal 来做的

BigDecimal a = new BigDecimal("1.0");
BigDecimal b = new BigDecimal("0.9");
BigDecimal c = new BigDecimal("0.8");

BigDecimal x = a.subtract(b);
BigDecimal y = b.subtract(c);

System.out.println(x); /* 0.1 */
System.out.println(y); /* 0.1 */
System.out.println(Objects.equals(x, y)); /* true */

16、对象的相等和对象引用相等的区别

new 创建对象实例(对象实例在堆内存中),对象引用指向对象实例(对象引用存放在栈内存中)。一个对象引用可以指向 0 个或 1 个对象,一个对象可以有 n 个引用指向它。

  • 对象的相等一般比较的是内存中存放的内容是否相等
  • 引用相等一般比较的是他们指向的内存地址是否相等

17、类的构造方法的作用是什么

构造方法是一种特殊的方法,主要作用是完成对象的初始化工作。构造方法的特性是:

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

在Java中定义一个不做事且没有参数的构造方法有什么作用?

  • Java 程序在执行子类的构造方法之前,如果没有用 super() 来调用父类特定的构造方法,则会调用父类中“没有参数的构造方法”
  • 因此,如果父类中只定义了有参数的构造方法,而在子类的构造方法中又没有用 super() 来调用父类中特定的构造方法,则编译时将发生错误,因为 Java 程序在父类中找不到没有参数的构造方法可供执行。解决办法是:在父类里加上一个不做事且没有参数的构造方法。

18、如果一个类没有声明构造方法,该程序能正确执行吗

如果一个类没有声明构造方法,也可以执行!因为一个类即使没有声明构造方法也会有默认的不带参数的构造方法。如果我们自己添加了类的构造方法(无论是否有参),Java 就不会再添加默认的无参数的构造方法了,我们一直在不知不觉地使用构造方法,这也是为什么我们在创建对象的时候后面要加一个括号(因为要调用无参的构造方法)。

如果我们重载了有参的构造方法,记得都要把无参的构造方法也写出来(无论是否用到),因为这可以帮助我们在创建对象的时候少踩坑

19、构造方法有哪些特点?是否可以被override

构造方法特点如下:

  • 名字与类名相同。
  • 没有返回值,但不能用 void 声明构造函数。
  • 生成类的对象时自动执行,无需调用。
public class Student 
    // 两个成员变量 私有
    private String name;
    private int age;
    
    
    // 无参构造器
    public Student()
        
    
    
    
    // 有参数构造器
    public Student(String name,int age)
        this.name = name;
        this.age = age;
    

构造方法不能被 override(重写),但是可以 overload(重载),所以你可以看到一个类中有多个构造函数的情况。

20、字符串拼接用"+"还是StringBuilder

String str1 = "he";
String str2 = "llo";
String str3 = "world";
String str4 = str1 + str2 + str3;

字符串对象通过“+”的字符串拼接方式,实际上是通过 StringBuilder 调用 append() 方法实现的,拼接完成之后调用 toString() 得到一个 String 对象 。

Java用作字符串拼接有哪些方法?

  1. 加号拼接

  2. StringBuilder 的 append

  3. StringBuffer 的 append

  4. String 的 concat

在拼接少数字符串(不超过4个)的时候,concat效率是最高的

多个字符串拼接的时候,StringBuilder/StringBuffer的效率是碾压的。

21、String中的equals()和Object中的equals()

String 中的 equals 方法是被重写过的,比较的是 String 字符串的值是否相等。 Objectequals 方法是比较的对象的内存地址。

22、字符串常量池了解过吗

字符串常量池 是 JVM 为了提升性能和减少内存消耗针对字符串(String 类)专门开辟的一块区域,主要目的是为了避免字符串的重复创建

// 在堆中创建字符串对象”ab“
// 将字符串对象”ab“的引用保存在字符串常量池中
String aa = "ab";
// 直接返回字符串常量池中字符串对象”ab“的引用
String bb = "ab";
System.out.println(aa==bb);// true

String s1 = new String(“abc”) 这句话创建了几个字符串对象?

  • 会创建 1 或 2 个字符串对象
  1. 如果字符串常量池中不存在字符串对象“abc”的引用,那么会在堆中创建 2 个字符串对象“abc”

    String s1 = new String("abc");
    
  2. 如果字符串常量池中已存在字符串对象“abc”的引用,则只会在堆中创建 1 个字符串对象“abc”

    // 字符串常量池中已存在字符串对象“abc”的引用
    String s1 = "abc";
    // 下面这段代码只会在堆中创建 1 个字符串对象“abc”
    String s2 = new String("abc");
    

23、try-catch-finally如何使用

  • try块 : 用于捕获异常。其后可接零个或多个 catch 块,如果没有 catch 块,则必须跟一个 finally 块。
  • catch块 : 用于处理 try 捕获到的异常。
  • finally 块 : 无论是否捕获或处理异常,finally 块里的语句都会被执行。当在 try 块或 catch 块中遇到 return 语句时,finally 语句块将在方法返回之前被执行

注意:不要在 finally 语句块中使用 return,当 try 语句 和 finally 语句都有 return 语句时, try 语句块中的 return 语句会被忽略。

24、finally 中的代码一定会执行吗

不一定的!在某些情况下,finally 中的代码不会被执行。比如说 finally 之前虚拟机被终止运行的话,finally 中的代码就不会被执行。

try 
    System.out.println("Try to do something");
    throw new RuntimeException("RuntimeException");
 catch (Exception e) 
    System.out.println("Catch Exception -> " + e.getMessage());
    // 终止当前正在运行的Java虚拟机
    System.exit(1);
 finally 
    System.out.println("Finally");


输出:

Try to do something
Catch Exception -> RuntimeException

另外,在以下 2 种特殊情况下,finally 块的代码也不会被执行:

  1. 程序所在的线程死亡。
  2. 关闭 CPU。

25、如何使用try-with-resources代替try-catch-finally

  • 资源的定义:任何实现 java.lang.AutoCloseable或者 java.io.Closeable 的对象
  • 关闭资源和 finally 块的执行顺序:在 try-with-resources 语句中,任何 catch 或 finally 块在声明的资源关闭后运行

Java 中类似于InputStreamOutputStreamScannerPrintWriter等的资源都需要我们调用close()方法来手动关闭,一般情况下我们都是通过try-catch-finally语句来实现这个需求,如下:

//读取文本文件的内容
Scanner scanner = null;
try 
    scanner = new Scanner(new File("D://read.txt"));
    while (scanner.hasNext()) 
        System.out.println(scanner.nextLine());
    
 catch (FileNotFoundException e) 
    e.printStackTrace();
 finally 
    if (scanner != null) 
        scanner.close();
    

使用try-with-resources 语句改造上面的代码:

try (Scanner scanner = new Scanner(new File("test.txt"))) 
    while (scanner.hasNext()) 
        System.out.println(scanner.nextLine());
    
 catch (FileNotFoundException fnfe) 
    fnfe.printStackTrace();

当然多个资源需要关闭的时候,通过使用分号分隔,可以在try-with-resources块中声明多个资源。

26、Java中是否可以重写一个private或者static方法

Java 中 static 方法不能被重写,因为方法重写是基于运行时动态绑定的,而 static 方法是编译时静态绑定的。

Java 中也不可以重写 private 的方法,因为 private 修饰的变量和方法只能在当前类中使用, 如果是其他的类继承当前类是不能访问到 private 变量或方法的,当然也不能重写。

注意:静态方法和私有方法不可以被重写

27、Java中创建对象的几种方式

  1. 使用 new 关键字
  2. 使用 Class 类的 newInstance 方法,该方法调用无参的构造器创建对象(反射):Class.forName.newInstance();
  3. 使用 clone() 方法(实现 Cloneable 接口并重写 Object 类中的 clone() 方法,实现的是浅克隆)
  4. 反序列化,比如调用 ObjectInputStream 类的 readObject() 方法(实现 Serializable 接口,通过对象的序列化和反序列化实现克隆,可以实现真正的深克隆)

28、抽象类和接口有什么区别

  1. 抽象类中可以有抽象方法和具体方法,而接口中只能有抽象方法(public abstract)
  2. 抽象类中的成员权限可以是 public、默认、protected(抽象类中抽象方法就是为了重写,所以不能被 private 修饰),而接口中的成员只可以是 public(方法默认:public abstrat、成员变量默认:public static final)
  3. 抽象类可以有静态代码块和静态方法,而接口中不能含有静态代码块以及静态方法

设计层面上的区别:

  • 抽象类是对一种事物的抽象,即对类抽象,而接口是对行为的抽象。抽象类是对整个类整体进行抽象,包括属性、行为,但是接口却是对类局部(行为)进行抽象。
  • 设计层面不同,抽象类作为很多子类的父类,它是一种模板式设计。而接口是一种行为规范,它是一种辐射式设计

抽象类能使用 final 修饰吗?

不能,定义抽象类就是让其他类继承的,如果定义为 final 该类就不能被继承,这样彼此就会产生矛盾,所以 final 不能修饰抽象类。

29、语法问题

short s1 = 1;
s1 = s1 + 1; // 报错,在 s1 + 1 运算时会自动提升表达式的类型为 int ,那么将 int 型值赋值给 short 型变量,s1 会出现类型转换错误

short s1 = 1;
s1 += s1; // 正确: += 是 Java 语言规定的运算符,Java 编译器会对它进行特殊处理,因此可以正确编译

30、Integer和int的区别

  • int 是 Java 的八种基本数据类型之一,而 Integer 是 Java 为 int 类型提供的封装类;
  • int 型变量的默认值是 0,Integer 变量的默认值是 null,这一点说明 Integer 可以区分出未赋值和值为 0 的区分;
  • Integer 变量必须实例化后才可以使用,而 int 不需要。

Integer 和 int 的比较延伸

  1. 由于 Integer 变量实际上是对一个 Integer 对象的引用,所以两个通过 new 生成的 Integer 变量永远是不相等的,因为其内存地址是不同的
  2. **Integer 变量和 int 变量比较时,只要两个变量的值是相等的,则结果为 true。**因为包装类 Integer 和基本数据类型 int 类型进行比较时,Java 会自动拆包装类为 int,然后进行比较,实际上就是两个 int 型变量在进行比较
  3. 非 new 生成的 Integer 变量和 new Integer() 生成的变量进行比较时,结果为 false。因为非 new 生成的 Integer 变量指向的是 Java 常量池中的对象,而 new Integer() 生成的变量指向中新建的对象,两者在内存中的地址不同
  4. 对于两个非 new 生成的 Integer 对象进行比较时,如果两个变量的值在区间 [-128, 127] 之间,则比较结果为 true,否则为 false。

31、final、finally、finalize的区别

final:用于声明变量、方法和类,分别表示变量不可变、方法不可重写、被其修饰的类不可继承;

finally:异常处理语句结构的一部分,表示总是执行;

finallize:Object类的一个方法,在垃圾回收时会调用被回收对象的finalize

32、两个对象的 hashCode() 相同,则 equals()也一定为true吗

两个对象的 hashCode() 相同,equals() 不一定为 true。因为在散列表中,hashCode() 相等即两个键值对的哈希值相等,然而哈希值相等,并不一定能得出键值对相等【散列冲突】。

33、为什么重写equals()就一定要重写 hashCode()方法

这个问题应该是有个前提,就是你需要用到 HashMap、HashSet 等 Java 集合,用不到哈希表的话,其实仅仅重写 equals() 方法也可以。而工作中的场景是常常用到 Java 集合,所以 Java 官方建议重写 equals() 就一定要重写 hashCode() 方法。

对于对象集合的判重,如果一个集合含有 10000 个对象实例,仅仅使用 equals() 方法的话,那么对于一个对象判重就需要比较 10000 次,随着集合规模的增大,时间开销是很大的。但是同时使用哈希表的话,就能快速定位到对象的大概存储位置,并且在定位到大概存储位置后,后续比较过程中,如果两个对象的 hashCode 不相同,也不再需要调用 equals() 方法,从而大大减少了 equals() 比较次数。

所以从程序实现原理上来讲的话,既需要 equals() 方法,也需要 hashCode() 方法。那么既然重写了 equals(),那么也要重写 hashCode() 方法,以保证两者之间的配合关系

34、&和&&的区别

Java 中 && 和 & 都是表示与的逻辑运算符,都表示逻辑运输符 and,当两边的表达式都为 true 的时候,整个运算结果才为 true,否则为 false。

  • &&:有短路功能,当第一个表达式的值为 false 的时候,则不再计算第二个表达式
  • &:不管第一个表达式结果是否为 true,第二个都会执行。除此之外,& 还可以用作位运算符:当 & 两边的表达式不是 Boolean 类型的时候,& 表示按位操作

35、Java中的Math.round(-1.5)等于多少

long l1 = Math.round(-1.5); // -1
long l2 = Math.round(1.5);  // 2

因为在数轴上取值时,中间值(0.5)向右取整,所以正 0.5 是往上取整,负 0.5 是直接舍弃

36、Java中的动态代理是什么?有哪些应用

动态代理:当想要给实现了某个接口的类中的方法,加一些额外的处理。比如说加日志,加事务等。可以给这个类创建一个代理,故名思议就是创建一个新的类,这个类不仅包含原来类方法的功能,而且还在原来的基础上添加了额外处理的新功能。这个代理类并不是定义好的,是动态生成的。具有解耦意义,灵活,扩展性强。

动态代理的应用:Spring 的 AOP 、加事务、加权限、加日志。

Java中,实现动态代理有两种方式:

  1. JDK 动态代理:java.lang.reflect 包中的 Proxy 类和 InvocationHandler 接口提供了生成动态代理类的能力
  2. Cglib 动态代理:Cglib (Code Generation Library )是一个第三方代码生成类库,运行时在内存中动态生成一个子类对象从而实现对目标对象功能的扩展。

JDK 动态代理和 Cglib 动态代理的区别:

  1. JDK 的动态代理有一个限制,就是使用动态代理的对象必须实现一个或多个接口
  2. cglib 代理的对象则无需实现接口,达到代理类无侵入。(如果想代理没有实现接口的类,就可以使用 CGLIB实现)

37、字节和字符的区别

字节是存储容量的基本单位,字符是数字、字母、汉字以及其他语言的各种符号。1字节 = 8 个二进制位,一个字符由一个字节或者多个字节的二进制单位组成。

38、值传递和引用传递有什么区别

  • 值传递,按值调用(call by value):指的是在方法调用时,传递的参数是值的拷贝,传递后就互不相关了
  • 引用传递,引用调用(call by reference):指的是在方法调用时,传递的参数是引用变量所对应的内存地址。传递前和传递后都指向同一个引用(也就是同一个内存空间)

为什么Java中只有值传递?

  • Java采用按值调用。也就是说,方法得到的是所有参数值的一个拷贝,也就是说,方法不能修改传递给它的任何参数变量的内容。

39、BIO、NIO、AIO有什么区别

先来回顾一下这样几个概念:同步与异步,阻塞与非阻塞。

  • 同步: 同步就是发起一个请求,被调用者未处理完请求之前,调用不返回。
  • 异步: 异步就是发起一个请求,立刻得到被调用者的响应表示已接收到请求,但是被调用者并没有返回请求处理结果,此时我们可以处理其他的请求,被调用者通过事件和回调等机制来通知调用者其返回结果。

同步和异步的区别在于调用者需不需要等待被调用者的处理结果。

  • 阻塞: 阻塞就是发起一个请求,调用者一直等待请求结果返回,也就是当前线程会被挂起,无法从事其他任务,只有当返回结果才能继续。
  • 非阻塞: 非阻塞就是发起一个请求,调用者不用一直等着结果返回,可以先去干其他事情。

阻塞和非阻塞的区别在于调用者的线程需不需要挂起。

  • BIO(jdk1.4之前):Block IO 同步阻塞式 IO,就是我们平常使用的传统 IO,它基于流模型实现,一个连接一个线程,客户端有连接请求时,服务器端就需要启动一个线程进行处理,线程开销大。

  • NIO:Non IO 同步非阻塞 IO,是传统 IO 的升级,客户端发送的连接请求都会注册到多路复用器上,多路复用器轮询到连接有I/O请求时才启动一个线程进行处理。

    • NIO 能解决什么问题?通过一个固定大小的线程池,来负责管理工作线程,避免频繁创建、销毁线程的开销,这是我们构建并发服务的典型方式
  • AIO(jdk 1.7过后 又叫NIO 2):异步非堵塞 IO,是 NIO 的升级,异步 IO 的操作基于事件和回调机制,性能是最好的。也就是应用操作之后会直接返回,不会堵塞在那里,当后台处理完成,操作系统会通知相应的线程进行后续的操作。

40、访问修饰符public、private、protected以及不写时的区别

Java 支持 4 种不同的访问权限。

  • default (即默认,什么也不写): 在同一包内可见,不使用任何修饰符。使用对象:类、接口、变量、方法。
  • private : 在同一类内可见。使用对象:变量、方法。 注意:不能修饰类
  • public : 对所有类可见。使用对象:类、接口、变量、方法
  • protected : 对同一包内的类和所有子类可见。使用对象:变量、方法。 注意:不能修饰类

41、final关键字的作用

final 关键字:

  • 修饰类:类不可被继承
  • 修饰方法:方法不能被重写
  • 修饰变量:变量有且仅能被赋值一次
  1. 修饰成员变量

    • 如果 final 修饰的是类变量,只能在静态初始块中指定初始值或者声明该类变量时指定初始值
    • 如果 final 修饰的是实例成员变量,可以在非静态初始块、声明该变量或者构造器中执行初始值
    public class Hello
        // 有static修饰的就是类变量,静态成员变量,
        final static int a = 0;  // 在声明的时候就要赋值,或者静态代码块赋值
        static 
            a = 0;
        
        // 没有 static 修饰的是实例成员变量,在声明的时候就需要赋值,或者代码块中赋值,或者构造器赋值
        final int b = 0;
        
            b = 0;
        
        
        
    
    
    1. 修饰基本类型数据和引用类型数据