Java基础面试题

1.JDK和JRE的区别?
  • JDK:Java Development Kit 的简称,Java 开发工具包,提供了 Java 的开发环境和运行环境。
  • JRE:Java Runtime Environment 的简称,Java 运行环境,为 Java 的运行提供了所需环境。
2.==和equals的区别?
  • 基本类型:比较的是值是否相同;
  • 引用类型:比较的是引用是否相同;
String str1 = "abc"; 
String str2 = "abc"; 
System.out.println(str1==str2);//true 
String str3 =newString("abc"); 
String str4 =newString ("abc");
System.out.println(str3==str4);//false
System.out.println(str3.equals(str4));//true

解析:equals 本质上就是 ==,只不过 String 和 Integer 等重写了 equals 方法,把它变成了值比较。
PS:

Integer i1 = 100, i2 = 100;
Integer i3 = 1000, i3 = 1000;
System.out.println(i1==i2);//true
System.out.println(i3==i4);//false

解析:在整数的包装类当中,在第一次创建 Integer 类的对象的时候,都会首先创建好缓存数组。当需要包装的 值是在 IntegerCache 数组当中的元素的时候,就会返回数组当中的Integer 对象。JVM 默认就会设置数组的范围为 -128 ~ 127 。如下:

@HotSpotIntrinsicCandidate
public static Integer valueOf(int i) {
    if (i >= IntegerCache.low && i <= IntegerCache.high)
        return IntegerCache.cache[i + (-IntegerCache.low)];
    return new Integer(i);
}
3.Java基本数据类型
byte short int long float double char boolean, String 属于对象。
4.两个对象的 hashCode()相同,则 equals()也一定为 true,对吗?

    不对,两个对象的 hashCode() 相同,equals() 不一定 true。

5. final 在 java 中有什么作用?
  • final 修饰的类叫最终类,该类不能被继承。
  • final 修饰的方法不能被重写。
  • final 修饰的变量叫常量,常量必须初始化,初始化之后值就不能被修改。
6. java 中的 Math.round(-1.5) 等于多少?

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

7.String、StringBuffer、StringBuilder的区别?

    String 是字符串常量, final 修饰; StringBuffer 字符串变量(线程安全); StringBuilder 字符串变量(线程不安全).此外 StringBuilder 和 StringBuffer 实现原理一样,都是基于数组扩容来实现的.
    StringBuffer 和 StringBuilder 的实现原理一样,其父类都是 AbstractStringBuilder.StringBuffer 是线程安全的, StringBuilder 是 JDK 1.5 新增的,其功能和 StringBuffer 类似,但是非线程安全.因此,在没有多线程问题的前提下,使用 StringBuilder 会取得更好的性能.

8. String str=“i” 与 String str=new String(“i”) 一样吗?

    不一样,因为内存的分配方式不一样。String str=“i” 的方式,java 虚拟机会将其分配到常量池中;而 String str=new String(“i”) 则会被分到堆内存中。

9. 如何将字符串反转?

1)使用 StringBuilder 或者 StringBuffer 的 reverse() 方法。

String str = "abcdefg";
StringBuffer stringBuffer = new StringBuffer(str);
StringBuilder stringBuilder = new StringBuilder(str);
System.out.println(stringBuffer.reverse());
System.out.println(stringBuilder.reverse());

2)使用 String 对象的 toCharArray() 方法,将字符串转换为 char[] 数组,然后逆向遍历并拼接。

String str = "abcdefg";
StringBuilder strNew =new StringBuilder();
char[] chars = str.toCharArray();
for(int i = chars.length -1; i >= 0; i--){
    strNew.append(chars[i]);
}

3)使用 String 对象的 charAt 方法逐个获取字符,正向遍历,逆向拼接

String str = "abcdefg";
StringBuilder strNew =new StringBuilder();
for (int i = 0; i < str.length(); i++) {
    strNew.insert(0, str.charAt(i));
}
10.String类的常用方法有哪些?
  • indexOf():返回指定字符的索引。
  • charAt():返回指定索引处的字符。
  • replace():字符串替换。
  • trim():去除字符串两端空白。
  • split():分割字符串,返回一个分割后的字符串数组。
  • getBytes():返回字符串的 byte 类型数组。
  • length():返回字符串长度。
  • toLowerCase():将字符串转成小写字母。
  • toUpperCase():将字符串转成大写字符。
  • substring():截取字符串。
  • equals():字符串比较。
11.普通类和抽象类的区别?抽象类可以用final修饰吗?抽象类和接口的区别?

  1)普通类不能有抽象方法,抽象类可以有也可以没有抽象方法,抽象类不能直接实例化,普通类可以。
  2)抽象类不能用 final 修饰,因为抽象类的定义就是为了被继承。
  3)抽象类和接口的区别:
      抽象类使用 extends 继承,接口使用 implements 实现;
      抽象类可以有构造函数,接口不能有;
      抽象类可以有非抽象接口,接口不能有(java8 之后可以有了)
      类可以实现多个接口,但是只能继承一个类;
      接口中的方法默认使用 public 访问修饰符,抽象类中的方法可以任意访问修饰符(抽象方法不能使用 private)

12.Java中的IO流分为几种?
  • 按功能划分:输入流、输出流;
  • 按类型划分:字节流、字符流,字节流按 8 为传输以字节为单位,字符流按 16 为传输以字符为单位;
13.BIO、NIO、AIO有什么区别?
  • BIO: Block IO 同步阻塞式 IO,即传统 IO,特点是使用简单,并发处理能力低。
  • NIO: Non IO 同步非阻塞 IO,传统 IO的升级,客户端和服务端通过 Channel 通讯,实现了多路复用。
  • AIO: Asynchronous IO 是 NIO 的升级,也叫 NIO2,实现了异步非阻塞 IO,异步 IO 的操作基于事件和回调机制。
14.Files的常用方法都有哪些?
  • Files.exists():检测文件路径是否存在;
  • Files.createFile():创建文件;
  • Files.createDirectory():创建文件夹;
  • Files.delete():删除一个文件或目录;
  • Files.copy():复制文件;
  • Files.move():移动文件;
  • Files.size():查看文件个数;
  • Files.read():读取文件;
  • Files.write():写入文件;
15.面向对象的特征

    封装、继承、多态

16.父类的静态方法能否被子类重写

    不能.重写只适用于实例方法,不能用于静态方法,而子类当中含有和父类相同签名的静态方法。

17.什么是不可变对象?好处是什么?

    不可变对象指对象一旦被创建,状态就不能再改变,任何修改都会创建一个新的对象,如 String、Integer 及其它包装类.不可变对象最大的好处是线程安全.

18.静态变量和实例变量的区别?

    静态变量存储在方法区,属于类所有.实例变量存储在堆当中,其引用存在当前线程栈.需要注意的是从 JDK1.8 开始用于实现方法区的 PermSpace 被 MetaSpace 取代了.

19.java 创建对象的几种方式
  • new 创建新对象
  • 通过反射机制
  • 采用 clone 机制
  • 通过序列化机制

  PS:前两者都需要显式地调用构造方法. 对于 clone 机制,需要注意浅拷贝和深拷贝的区别,对于序列化机制需要明确其实现原理,在 java 中序列化可以通过实现 Externalizable 或者 Serializable 来实现.

20.Object中有哪些公共方法?

    equals(),clone(),getClass(),notify(),notifyAll(),wait(),toString()

21.& 和 &&的区别

    基础的概念不能弄混: & 是位操作, && 是逻辑运算符.需要记住逻辑运算符具有短路特性,而 & 不具备短路特性.

22.在.java文件内部可以有多少类(非内部类)?

    在一个 java 文件中只能有一个 public 公共类,但是可以有多个 default 修饰的类.

23.如何正确的退出多层嵌套循环?
  • 使用标号和 break;
  • 通过在外层循环中添加标识符
24.final,finalize()和finally{}的不同之处

    三者没有任何相关性,遇到有问着问题的面试官就拖出去砍了吧.

  • final是一个修饰符,用于修饰变量,方法和类.如果 final 修饰变量,意味着该变量的值在初始化后不能被改变.
  • finalize() 方法是在对象被回收之前调用的方法,给对象自己最后一个复活的机会.但是该方法由 Finalizer 线程调用,但调用时机无法保证.
  • finally是一个关键字,与 try 和 catch 一起用于异常的处理, finally{} 一定会被执行,在此处我们通常用于资源关闭操作.
25.clone()是哪个类的方法?

    java.lang.Cloneable 是一个标示性接口,不包含任何方法.clone ()方法在 Object 类中定义的一个Native方法:

protected NativeObject clone() throws CloneNotSupportedException;
26.深拷贝和浅拷贝的区别是什么?
  • 浅拷贝:被复制对象的所有变量都含有与原来的对象相同的值,而所有的对其他对象的引用仍然指向原来的对象.换言之,浅拷贝仅仅复制所考虑的对象,而不复制它所引用的对象.
  • 深拷贝:被复制对象的所有变量都含有与原来的对象相同的值.而那些引用其他对象的变量将指向被复制过的新对象.而不再是原有的那些被引用的对象.换言之.深拷贝把要复制的对象所引用的对象都复制了一遍.
27.64位的JVM当中,int的长度是多少?

    Java中数据类型所占用的位数和平台无关,在 32 位和 64 位 的Java 虚拟机中,int 类型的长度都是占 4 字节.
  PS:short(2)、int(4)、long(8)、float(4)、double(8)

28.int和Integer的区别?

    Integer 是 int 的包装类型,在拆箱和装箱中,二者自动转换.int是基本类型,直接存数值;而integer是对象;用一个引用指向这个对象.由于Integer是一个对象,在JVM中对象需要一定的数据结构进行描述,相比int而言,其占用的内存更大一些.

29.String s = new String(“abc”) 创建了几个String对象?

    2 个.一个是字符串字面常数,在字符串常量池中;另一个是new出来的字符串对象,在堆中.

30.请问s1 == s3 是 true 还是 false,s1 == s4 是 false 还是true? s1 == s5 呢?
String s1 = "abc";
String s2 = "a";
String s3 = s2 + "bc";
String s4 = "a" + "bc";
String s5 = s3.intern();
s1==s3返回false,s1==s4返回true,s1==s5返回true.

解析:

  • “abc" 这个字符串常量值会直接方法字符串常量池中,s1 是对其的引用.由于 s2 是个变量,编译器在编译期间无法确定该变量后续会不会改,因此无法直接将 s3 的值在编译器计算出来,因此 s3 是堆中 “abc” 的引用.因此 s1!=s3.
  • 对于 s4 而言,其赋值号右边是常量表达式,因此可以在编译阶段直接被优化为 "abc”,由于 “abc” 已经在字符串常量池中存在,因此 s4 是对其的引用,此时也就意味 s1 和 s4 引用了常量池中的同一个 “abc”. 所以s1==s4.
  • String 中的 intern() 会首先从字符串常量池中检索是否已经存在字面值为 “abc” 的对象,如果不存在则先将其添加到字符串常量池中,否则直接返回已存在字符串常量的引用.此处由于 “abc” 已经存在字符串常量池中了,因此 s5 和 s1 引用的是同一个字符串常量.
31.以下代码中,s5==s2返回值是什么?
String s1="ab";
String s2="a"+"b";
String s3="a";
String s4="b";
String s5=s3+s4;

返回 false. 在编译过程中,编译器会将 s2 直接优化为 “ab”,将其放置在常量池当中;而 s5 则是被创建在堆区,相当于 s5=new String(“ab”);

32.你对String对象的intern()熟悉么?

    Stirng 中的 intern() 是个Native方法,它会首先从常量池中查找是否存在该常量值的字符串,若不存在则先在常量池中创建,否则直接返回常量池已经存在的字符串的引用.

33.什么是编译器常量?使用它有什么风险?

    公共静态不可变,即 public static final 修饰的变量就是我们所说的编译期常量.这里的 public 可选的.实际上这些变量在编译时会被替换掉,因为编译器明确的能推断出这些变量的值(如果你熟悉C++,那么这里就相当于宏替换).
    编译器常量虽然能够提升性能,但是也存在一定问题:你使用了一个内部的或第三方库中的公有编译时常量,但是这个值后面被其他人改变了,但是你的客户端没有重新编译,这意味着你仍然在使用被修改之前的常量值.

34. 3*0.1==0.3返回值是什么

    false,因为有些浮点数不能完全精确的表示出来.

35.java当中使用什么类型表示价格比较好?

    如果不是特别关心内存和性能的话,使用 BigDecimal.否则使用预定义精度的 double 类型.

36.如何将byte转为String

    可以使用 String 接收 byte[] 参数的构造器来进行转换,注意要使用的正确的编码,否则会使用平台默认编码.这个编码可能跟原来的编码相同.也可能不同.

String str = new String("abc".getBytes(), StandardCharsets.UTF_8);
37.可以将int强转为byte类型么?会产生什么问题?

    可以做强制转换,但是 Java 中int是 32 位的而byte是 8 位的.如果强制转化 int 类型的高 24 位将会被丢弃, byte 类型的范围是从-128到128.

38.a=a+b与a+=b有什么区别吗?

    += 操作符会进行隐式自动类型转换,此处a+=b隐式的将加操作的结果类型强制转换为持有结果的类型,而a=a+b则不会自动进行类型转换.如:

39.了解泛型么? T、R、K、V、E分别指什么?简述泛型的上界和下界?

    常用的范型含义:

  • T - Type(类型)
  • R - Result(结果)
  • K - Key(键)
  • V - Value(值)
  • E - Element(元素)
  • N - Number(数字)
  • ? - 不确定类型

    有时候希望传入的类型有一个指定的范围,从而可以进行一些特定的操作,这时候就需要通配符了?在Java中常见的通配符主要有以下几种:

  • <?>: 无限制通配符;
  • <? extends E>: extends 关键字声明了类型的上界,表示参数化的类型可能是所指定的类型,或者是此类型的子类;
  • <? super E>: super关键字声明了类型的下界,表示参数化的类型可能是指定的类型,或者是此类型的父类;

它们的目的都是为了使方法接口更为灵活,可以接受更为广泛的类型;

  • <? extends E>: 用于灵活读取,使得方法可以读取 E 或 E 的任意子类型的容器对象;
  • <? super E>: 用于灵活写入或比较,使得对象可以写入父类型的容器,使得父类型的比较方法可以应用于子类对象;

    用简单的一句话来概括就是为了获得最大限度的灵活性,要在表示生产者或者消费者的输入参数上使用通配符,使用的规则就是:生产者有上限(读操作使用extends),消费者有下限(写操作使用super).

39.Java到底是值传递还是引用传递?

    Java 中方法参数严格来说是按值传递的。

  • 如果参数是基本类型:传递的是基本类型的字面量值的拷贝。
  • 如果参数是引用类型:传递得是该参量所引用的对象在队中地址的拷贝。
40.char类型可以存储中文汉字吗?

    可以的,因为 Java 中使用的编码时 Unicode,一个 char 类型占 2 个字节(16 Bit),而一个中文也是占 2 个字节,所以存放一个中文时没有问题的。

41.重载和重写的区别?

    方法的重写(Overriding)和重载(Overloading)是java多态性的不同表现,重写是父类与子类之间多态性的一种表现,重载可以理解成多态的具体表现形式。

  • 方法重载是一个类中定义了多个方法名相同,而他们的参数的数量不同或数量相同而类型和次序不同,则称为方法的重载(Overloading)。
  • 方法重写是在子类存在方法与父类的方法的名字相同,而且参数的个数与类型一样,返回值也一样的方法,就称为重写(Overriding)。
  • 方法重载是一个类的多态性表现,而方法重写是子类与父类的一种多态性表现。
42.Java异常有哪些分类?常见的异常有哪些?

异常分类:

  • Throwable:是 Java 异常的顶级类。
  • Error:是非程序异常类,即程序不能捕获的异常,一般是编译或者系统性错误,如:OutOfMemorry 内存溢出异常等。
  • Exception:是程序异常类,由程序内部产生。Exception 又分为运行时异常、非运行时异常
    • 运行时异常:不用显示捕获的异常,如:NullPointerException、ArrayIndexOutOfBoundsException 等。
    • 非运行时异常:是程序必须处理的异常,如:IOException、ClassNotFoundException 等。

常见的异常:

  • NullPointerException:空指针异常,操作一个 null 对象的方法或属性时会抛出这个异常。
  • OutOfMemoryError:内存溢出异常,是指要分配的对象的内存超出了当前最大的堆内存,需要调整堆内存的大小以及优化程序。
  • IOException:IO异常,需要手工捕获。
  • FileNotFoundException:文件找不到异常,如果文件不存在就会抛出这种异常。
  • ClassNotFoundException:类找不到异常,即在类路径下不能加载指定的类。
  • ClassCastException:类转换异常,将一个不是该类的实例转换成这个类就会抛出这个异常。
  • NoSuchMethodException:没有这个方法异常,一般发生在反射调用方法时。
  • IndexOutOfBoundsException:索引越界异常,当操作字符串或者数组时经常遇到。
  • ArithmeticException:算数异常,发生在数字的算术运算时的异常,如:3/0
  • SQLException:SQL 异常,发生在操作数据库时的异常。
43.throw和throws的区别?
  • throw:是真实抛出一个异常
  • throws:是声明可能会抛出一个异常
44.try里面return,finally还会执行吗?
  • 不管 try 有没有 return, finally 都会执行
  • 在 try 中 return,在 finally 执行前会把结果保存起来。即使在 finally 中有修改也以 try 中保存的指为准,但如果是引用类型,修改的属性会以 finally 修改后为准。
  • 如果 try/finally 都有 return,直接返回 finally 中的 return。
45.静态方法可以直接调用非静态方法吗?

    不可以,静态方法只能直接调用静态方法。
    因为非静态方法的调用需要先创建对象,而调用静态方法不需要对象实例化。

46.静态内部类和普通内部类的区别?

    静态内部类可以不依赖于外部类实例被实例化,而普通内部类需要在外部类实例化后才能实例化。

47.内部类可以访问其外部类的成员吗?

    可以的,内部类可以访问创建它的外部类对象的成员,包括私有成员。

48.Java类初始化顺序是怎样的?
  • 类的初始化顺序:
    静态变量, 静态代码快 -》 实例变量(属性,实例代码块,构造方法)

  • 继承关系初始化顺序:
    父类静态成员,静态代码块 -》 子类静态成员,静态代码块 -》 父类实例变量(属性,实例代码块,构造方法)-》子类实例变量(属性,实例代码块,构造方法)

    相同等级的初始化的先后顺序,是直接依赖代码中初始化的先后顺序

49.为什么成员变量不建议用isXXX?

    对应阿里巴巴开发手册第一章的命名风格的第八条:POJO 类中的任何布尔类型的变量,都不要加 is 前缀,否则部分框架解析会引起序列化错误。

50.Java常用的元注解有哪些?
  • @Target:描述了注解修饰的对象范围,取值在java.lang.annotation.ElementType定义,常用的包括:
    METHOD:用于描述方法
    PACKAGE:用于描述包
    PARAMETER:用于描述方法变量
    TYPE:用于描述类、接口或enum类型
  • @Retention: 表示注解保留时间长短。取值在java.lang.annotation.RetentionPolicy 中,取值为:
    RetentionPolicy.SOURCE -------------注解将被编译器丢弃
    RetentionPolicy.CLASS -------------注解在 class 文件中可用,但会被 JVM 丢弃
    RetentionPolicy.RUNTIME ---------VM将在运行期也保留注释,因此可以通过反射机制读取注解的信息
  • @Documented
    Documented 注解的作用是:描述在使用 javadoc 工具为类生成帮助文档时是否要保留其注解信息。
  • @Inherited
    Inherited 注解的作用是:使被它修饰的注解具有继承性(如果某个类使用了被 @Inherited 修饰的注解,则其子类将自动具有该注解)。
51.Java金额计算怎么避免丢失精度?

    Java中的类型 float、double 用来做计算会有精度丢失问题,所以使用 BigDecimal
例如:

double totalAmount = 0.09;
double feeAmount = 0.02;
System.out.println(totalAmount - feeAmount);//结果:0.06999999999999999
BigDecimal bigDecimal = BigDecimal.valueOf(0.09);
BigDecimal bigDecimal2 = BigDecimal.valueOf(0.02);
System.out.println(bigDecimal.subtract(bigDecimal2));//结果0.07
52.Java语法糖是什么意思?

语法糖就是对现有语法的一个封装。
Java中的语法糖:

  • 泛型与类型擦除
  • 自动装箱与拆箱
  • 可变参数
  • 曾强for循环
  • 内部类
  • 枚举类
53.transient关键字是什么意思?
  • transient 修饰的成员变量不能被序列化;
  • transient 只作用于实现 Serializable 接口;
  • transient 只能用来修饰普通成员变量字段;
  • 不管有没有 transient 修饰,静态变量都不能被序列化
54.Java反射机制有什么用?反射机制的优缺点?Class类的常用方法有哪些?
  • Java反射机制有什么用?
    Java反射机制的核心是在程序运行时动态加载类并获取类的详细信息,从而操作类或对象的属性和方法。
  • 反射机制的优缺点?
    优点:可以动态执行,在运行期间根据业务功能动态执行方法、访问属性,最大限度发挥了 Java 的灵活性。
    缺点:对性能有影响,这类操作总是慢于直接执行 Java 代码。
  • Class类的常用方法有哪些?
    • Class.forName():动态加载类
    • newInstance():根据对象的class新建一个对象
    • getSuperclass():获取继承的父类
    • getInterfaces():获取继承的接口
    • getDeclaredFields():获取字段名称
    • getDeclaredMethods():获取当前类的所有方法
    • getConstructors():获取所有的构造函数
    • getModifiers():反射中获得修饰符
    • getPackage():反射中获取package
    • getField(String name):反射中获得域成员
    • getFields():获得域数组成员
    • isAnnotation():判断是否为注解类型
    • isPrimitive():判断是否为基本类型
    • isArray():判断是否为数组类型
    • isEnum():判断是否为枚举类型
    • getClassLoader():获得类的类加载器
    • getMethods():获得公共方法
55.Java反射可以访问私有方法和私有变量吗?

可以的,使用 .setAccessible(true);

public static void main(String[] args) throws Exception {
    User user = new User();
    user.setName("神雕");
    user.setAge(500);
    Class<? extends User> aClass = user.getClass();
    //访问私有成员变量
    Field age = aClass.getDeclaredField("age");
    age.setAccessible(true);
    System.out.println(age.get(user));
    //访问私有方法
    Method getAge = aClass.getDeclaredMethod("getAge");
    getAge.setAccessible(true);
    Object invoke = getAge.invoke(user);
    System.out.println(invoke);
}

class User{
    public String name;
    private int age;
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    private int getAge() {
        return age;
    }
    public void setAge(int age) {
        this.age = age;
    }
}
56.什么是宏变量和宏替换?

    首先来理解下宏变量:
    Java 中,一个用 final 定义的变量,不管它是类型的变量,只要用final定义了并同时指定了初始值,并且这个初始值是在编译时就被确定下来的,那么这个final变量就是一个宏变量。编译器会把程序所有用到该变量的地方直接替换成该变量的值,也就是说编译器能对宏变量进行宏替换。

final String a = "hello";
final String b = a;
final String c = getHello();

解析:a在编译期间就能确定下来,而b、c不行,所以a是宏变量,b、c不是。

57.什么是逃逸分析?

    逃逸分析(Escape Analysis)简单来讲就是,Java Hotspot 虚拟机可以分析新创建对象的使用范围,并决定是否在 Java 堆上分配内存的一项技术。
逃逸分析的 JVM 参数如下:

  • 开启逃逸分析:-XX:+DoEscapeAnalysis
  • 关闭逃逸分析:-XX:-DoEscapeAnalysis
  • 显示分析结果:-XX:+PrintEscapeAnalysis
    对象的逃逸状态:
  1. 全局逃逸(GlobalEscape)
    即一个对象的作用范围逃出了当前方法或者当前线程,有以下几种场景:
    对象是一个静态变量
    对象是一个已经发生逃逸的对象
    对象作为当前方法的返回值

  2. 参数逃逸(ArgEscape)
    即一个对象被作为方法参数传递或者被参数引用,但在调用过程中不会发生全局逃逸,这个状态是通过被调方法的字节码确定的。

  3. 没有逃逸
    即方法中的对象没有发生逃逸。

逃逸分析优化:
针对上面第三点,当一个对象没有逃逸时,可以得到以下几个虚拟机的优化。

  1. 锁消除
    我们知道线程同步锁是非常牺牲性能的,当编译器确定当前对象只有当前线程使用,那么就会移除该对象的同步锁。
    例如,StringBuffer 和 Vector 都是用 synchronized 修饰线程安全的,但大部分情况下,它们都只是在当前线程中用到,这样编译器就会优化移除掉这些锁操作。
    锁消除的 JVM 参数如下:
    开启锁消除:-XX:+EliminateLocks
    关闭锁消除:-XX:-EliminateLocks
    锁消除在 JDK8 中都是默认开启的,并且锁消除都要建立在逃逸分析的基础上。

  2. 标量替换
    首先要明白标量和聚合量,基础类型和对象的引用可以理解为标量,它们不能被进一步分解。而能被进一步分解的量就是聚合量,比如:对象。
    对象是聚合量,它又可以被进一步分解成标量,将其成员变量分解为分散的变量,这就叫做标量替换。
    这样,如果一个对象没有发生逃逸,那压根就不用创建它,只会在栈或者寄存器上创建它用到的成员标量,节省了内存空间,也提升了应用程序性能。
    标量替换的 JVM 参数如下:
    开启标量替换:-XX:+EliminateAllocations
    关闭标量替换:-XX:-EliminateAllocations
    显示标量替换详情:-XX:+PrintEliminateAllocations
    标量替换同样在 JDK8 中都是默认开启的,并且都要建立在逃逸分析的基础上。

  3. 栈上分配
    当对象没有发生逃逸时,该对象就可以通过标量替换分解成成员标量分配在栈内存中,和方法的生命周期一致,随着栈帧出栈时销毁,减少了 GC 压力,提高了应用程序性能。

58.什么是伪共享?有什么解决方案?

    CPU的缓存是以缓存行(cache line)为单位进行缓存的,当多个线程修改不同变量,而这些变量又处于同一个缓存行时就会影响彼此的性能。例如:线程 1 和线程 2 共享一个缓存行,线程1只读取缓存行中的变量 1,线程 2 修改缓存行中的变量 2,虽然线程1和线程2操作的是不同的变量,由于变量 1 和变量 2 同处于一个缓存行中,当变量2被修改后,缓存行失效,线程 1 要重新从主存中读取,因此导致缓存失效,从而产生性能问题。
    解决方案:
    可以通过填充来解决伪共享问题,Java8 中引入了@sun.misc.Contended注解来自动填充。

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.FIELD, ElementType.TYPE})
public @interface Contended {
    String value() default "";
}
59.Java8 都新增了哪些新特性?

    Java8 新增了非常多的特性,我们主要讨论以下几个:

  • Lambda 表达式 − Lambda 允许把函数作为一个方法的参数(函数作为参数传递到方法中)。
  • 方法引用 − 方法引用提供了非常有用的语法,可以直接引用已有Java类或对象(实例)的方法或构造器。与lambda联合使用,方法引用可以使语言的构造更紧凑简洁,减少冗余代码。
  • 默认方法 − 默认方法就是一个在接口里面有了一个实现的方法。
  • 新工具 − 新的编译工具,如:Nashorn引擎 jjs、 类依赖分析器jdeps。
  • Stream API −新添加的Stream API(java.util.stream) 把真正的函数式编程风格引入到Java中。
  • Date Time API − 加强对日期与时间的处理。
  • Optional 类 − Optional 类已经成为 Java 8 类库的一部分,用来解决空指针异常。
  • Nashorn, JavaScript 引擎 − Java 8提供了一个新的Nashorn javascript引擎,它允许我们在JVM上运行特定的javascript应用。
60.Java8 中的Lambda 表达式有什么用?

    Lambda 表达式,也可称为闭包,它是推动 Java 8 发布的最重要新特性。
    Lambda 允许把函数作为一个方法的参数(函数作为参数传递进方法中)。
    使用 Lambda 表达式可以使代码变的更加简洁紧凑。

lambda 表达式的语法格式如下:

(parameters) -> expression
或
(parameters) ->{ statements; }

以下是lambda表达式的重要特征:

  • 可选类型声明:不需要声明参数类型,编译器可以统一识别参数值。
  • 可选的参数圆括号:一个参数无需定义圆括号,但多个参数需要定义圆括号。
  • 可选的大括号:如果主体包含了一个语句,就不需要使用大括号。
  • 可选的返回关键字:如果主体只有一个表达式返回值则编译器会自动返回值,大括号需要指定明表达式返回了一个数值。

Lambda 表达式的简单例子:

// 1. 不需要参数,返回值为 5  
() -> 5  

// 2. 接收一个参数(数字类型),返回其2倍的值  
x -> 2 * x  

// 3. 接受2个参数(数字),并返回他们的差值  
(x, y) -> x – y  

// 4. 接收2个int型整数,返回他们的和  
(int x, int y) -> x + y  

// 5. 接受一个 string 对象,并在控制台打印,不返回任何值(看起来像是返回void)  
(String s) -> System.out.print(s)
61.Java8 中的Optional 类有什么用?

    Optional 类是一个可以为null的容器对象。如果值存在则isPresent()方法会返回true,调用 get() 方法会返回该对象。
    Optional 是个容器:它可以保存类型T的值,或者仅仅保存null。Optional提供很多有用的方法,这样我们就不用显式进行空值检测。
    Optional 类的引入很好的解决空指针异常。
以下是一个 java.util.Optional 类的声明:

public final class Optional<T> extends Object

使用:

public static void main(String args[]){
  Java8Tester java8Tester = new Java8Tester();
  Integer value1 = null;
  Integer value2 = new Integer(10);
    
  // Optional.ofNullable - 允许传递为 null 参数
  Optional<Integer> a = Optional.ofNullable(value1);
    
  // Optional.of - 如果传递的参数是 null,抛出异常 NullPointerException
  Optional<Integer> b = Optional.of(value2);
  System.out.println(java8Tester.sum(a,b));
}

public Integer sum(Optional<Integer> a, Optional<Integer> b){
  // Optional.isPresent - 判断值是否存在
  System.out.println("第一个参数值存在: " + a.isPresent());
  System.out.println("第二个参数值存在: " + b.isPresent());
  // Optional.orElse - 如果值存在,返回它,否则返回默认值
  Integer value1 = a.orElse(new Integer(0));
    
  //Optional.get - 获取值,值需要存在
  Integer value2 = b.get();
  return value1 + value2;
}
62.Java8 中的Stream 有什么用?

    Stream 是 Java8 中处理集合的关键抽象概念,它可以指定你希望对集合进行的操作,可以执行非常复杂的查找、过滤和映射数据等操作。
1)筛选与切片
  filter:过滤流中的某些元素
  limit(n):获取n个元素
  skip(n):跳过n元素,配合limit(n)可实现分页
  distinct:通过流中元素的 hashCode() 和 equals() 去除重复元素
  Stream stream = Stream.of(6, 4, 6, 7, 3, 9, 8, 10, 12, 14, 14);

Stream<Integer> newStream = stream.filter(s -> s > 5) //6 6 7 9 8 10 12 14 14
.distinct() //6 7 9 8 10 12 14
.skip(2) //9 8 10 12 14
.limit(2); //9 8
newStream.forEach(System.out::println);

2) 映射
  map:接收一个函数作为参数,该函数会被应用到每个元素上,并将其映射成一个新的元素。
  flatMap:接收一个函数作为参数,将流中的每个值都换成另一个流,然后把所有流连接成一个流。

List<String> list = Arrays.asList("a,b,c", "1,2,3");
//将每个元素转成一个新的且不带逗号的元素
Stream<String> s1 = list.stream().map(s -> s.replaceAll(",", ""));
s1.forEach(System.out::println); // abc 123

Stream<String> s3 = list.stream().flatMap(s -> {
    //将每个元素转换成一个stream
    String[] split = s.split(",");
    Stream<String> s2 = Arrays.stream(split);
    return s2;
});
s3.forEach(System.out::println); // a b c 1 2 3

3) 排序
  sorted():自然排序,流中元素需实现Comparable接口
  sorted(Comparator com):定制排序,自定义Comparator排序器

List<String> list = Arrays.asList("aa", "ff", "dd");
//String 类自身已实现Compareable接口
list.stream().sorted().forEach(System.out::println);// aa dd ff

Student s1 = new Student("aa", 10);
Student s2 = new Student("bb", 20);
Student s3 = new Student("aa", 30);
Student s4 = new Student("dd", 40);
List<Student> studentList = Arrays.asList(s1, s2, s3, s4);

//自定义排序:先按姓名升序,姓名相同则按年龄升序
studentList.stream().sorted((o1, o2) -> {
    if (o1.getName().equals(o2.getName())) {
        return o1.getAge() - o2.getAge();
    } else {
        return o1.getName().compareTo(o2.getName());
    }
}).forEach(System.out::println); 

4) 匹配、聚合操作
  allMatch:接收一个 Predicate 函数,当流中每个元素都符合该断言时才返回true,否则返回false
  noneMatch:接收一个 Predicate 函数,当流中每个元素都不符合该断言时才返回true,否则返回false
  anyMatch:接收一个 Predicate 函数,只要流中有一个元素满足该断言则返回true,否则返回false
  findFirst:返回流中第一个元素
  findAny:返回流中的任意元素
  count:返回流中元素的总个数
  max:返回流中元素最大值
  min:返回流中元素最小值

List<Integer> list = Arrays.asList(1, 2, 3, 4, 5);

boolean allMatch = list.stream().allMatch(e -> e > 10); //false
boolean noneMatch = list.stream().noneMatch(e -> e > 10); //true
boolean anyMatch = list.stream().anyMatch(e -> e > 4); //true

Integer findFirst = list.stream().findFirst().get(); //1
Integer findAny = list.stream().findAny().get(); //1

long count = list.stream().count(); //5
Integer max = list.stream().max(Integer::compareTo).get(); //5
Integer min = list.stream().min(Integer::compareTo).get(); //1
63.Java8 中的@Repeatable 注解有什么用?

    重复注解,即一个注解可以在一个类、方法或者字段上同时使用多次。
源码:

@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.ANNOTATION_TYPE)
public @interface Repeatable {
    Class<? extends Annotation> value();
}

Spring中可以使用多个扫描组件来扫描多个包的注解:

@ComponentScan
@ComponentScan
public class Configuration {

}
64.Java8 中的方法引用是指什么?

    方法引用是只需要使用方法的名字,而具体调用交给函数式接口,需要和Lambda表达式配合使用。
如:

List<String> list = Arrays.asList("a", "b", "c");
list.forEach(str -> System.out.print(str));
list.forEach(System.out::print);
65.Java8 中的函数式编程怎么用?

传统创建线程的方式:

Thread t1 = new Thread(new Runnable() {
    @Override
    public void run() {
        System.out.println("t1");
    }
});
t1.start();

函数式接口方式:

Thread t2 = new Thread(() -> System.out.println("t2"));
t2.start();

凡是被@FunctionalInterface注解的接口都可以使用函数式编程

@FunctionalInterface
public interface Runnable {
    public abstract void run();
}
66.怎么创建一个Stream 流

1). Stream.of 可变参数、数组

Stream<String> stream = Stream.of("A", "B", "C");
String[] values = new String[]{"A", "B", "C"};
stream = Stream.of(values);
System.out.println("stream : " + stream .collect(Collectors.joining()));

2). Arrays、List、Set、Map的stream()方法

String[] values = new String[]{"A", "B", "C"};
Stream<String> stream = Arrays.stream(values);//Arrays
stream = Arrays.asList(values).stream();//List
stream = new HashSet<>(Arrays.asList(values)).stream();//Set
HashMap<String, String> map = new HashMap<>();
stream = map.values().stream();//Map
System.out.println("stream3: " + stream.collect(Collectors.joining()));
  • 4
    点赞
  • 18
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
回答: Java基础面试题可以包括很多方面的知识,以下是一些常见的问题和答案: 1. 什么是JNI? JNI是Java Native Interface的缩写,它提供了一组API,用于实现Java和其他语言(主要是C和C++)之间的通信。JNI允许Java代码与本地已编译的代码进行交互,尽管这可能会降低平台的可移植性。\[2\] 2. JNI的步骤是什么? JNI的步骤包括以下几个部分: - 在Java类中编写带有native声明的方法。 - 使用javac命令编译Java类。 - 使用javah命令生成头文件。 - 使用C/C++实现本地方法。 - 生成动态连接库。 - 执行Java代码。\[1\] 3. 请解释一下super.getClass()方法的作用。 super.getClass()方法是用于获取当前对象的父类的Class对象。在给定的示例中,Test类继承自Date类,当调用super.getClass().getName()时,会返回Test类的名称。因此,输出的结果是"Test"。\[3\] 希望以上回答能够帮助你理解Java基础面试题。如果你有其他问题,请随时提问。 #### 引用[.reference_title] - *1* *2* [Java基础常见面试题及详细答案(总结40个)](https://blog.csdn.net/ayouki123456/article/details/124983188)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^control_2,239^v3^insert_chatgpt"}} ] [.reference_item] - *3* [Java基础面试题50题](https://blog.csdn.net/weixin_38337769/article/details/100560220)[target="_blank" data-report-click={"spm":"1018.2226.3001.9630","extra":{"utm_source":"vip_chatgpt_common_search_pc_result","utm_medium":"distribute.pc_search_result.none-task-cask-2~all~insert_cask~default-1-null.142^v91^control_2,239^v3^insert_chatgpt"}} ] [.reference_item] [ .reference_list ]

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值