Java语言特点

Q:你觉得Java语言有什么特点

A(啥也不会的我):额我觉得Java语言首先相较于C++好学一些,然后Java它是一个解释型语言,有面向对象的思想,另外Java是跨平台的,所以和平台的兼容性也更好一些。

A(改进版):Java是一种解释型语言的说法不太准确。Java是一种编译型语言,但它是将源代码编译成字节码,然后在Java虚拟机(JVM)上解释执行。这是Java实现跨平台的关键,因为只需在不同平台上实现不同的JVM即可。所以,你可以解释为Java是一种编译后解释执行的语言。

可以额外增加下面这三点:

  • 面向对象:Java是一种纯粹的面向对象编程语言,支持封装、继承和多态等面向对象的特性。
  • 平台无关性:Java的字节码可以在任何支持Java虚拟机(JVM)的平台上运行,使得Java具有很好的可移植性和平台无关性。
  • 垃圾回收:Java具有自动内存管理机制,通过垃圾回收器自动管理内存,减少了程序员手动管理内存的复杂性和风险。(补充一下C++的内存回收,事实上C++是有自动内存回收机制的,比如C++11引入的智能指针,但是相较于Java有所欠缺的。)

JavaSE vs JavaEE

Q:说一下这两者的区别

A(什么都不会的我):好像有个是企业版?有个更难一些?(面试官:这神经病在说什么屁话)

A(改进版):JavaSE,S是standard,所以是标准版。JavaEE,E是enterprise,所以是企业版。JavaSE是基础版本,JavaEE是高级版本。

JVM vs JDK vs JRE

Q:这三个分别是啥

A(啥都不会的我):JVM是Java虚拟机,是java代码编译后运行的平台。JRE是java的运行需要的环境,JDK除了包含运行环境外还包含开发环境。

A(改进版):上面说的差不多对,补充一下:JDK包含JRE包含JVM。

Java和C++的区别

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

Java数据类型

Q:Java数据类型了解过吗

A(啥也不会的我):了解过的,Java包括基础数据类型和引用类型,其中基础数据类型包括整形、布尔类型、浮点类型、字符类型。除了基础类型之外其他都是引用类型。字符串类型是引用类型,但是它的使用方式类似于基本数据类型。

A(补充版):基本数据类型存放在栈中是一个常见的误区! 基本数据类型的存储位置取决于它们的作用域和声明方式。如果它们是局部变量,那么它们会存放在栈中;如果它们是成员变量,那么它们会存放在堆中。Java 里使用 long 类型的数据一定要在数值后面加上 L,否则将作为整型解析。

equals和==的区别

1)对于==,比较的是值是否相等

如果作用于基本数据类型的变量,则直接比较其存储的 值是否相等,

如果作用于引用类型的变量,则比较的是所指向的对象的地址是否相等。

其实==比较的不管是基本数据类型,还是引用数据类型的变量,比较的都是值,只是引用类型变量存的值是对象的地址

2)对于equals方法,比较的是是否是同一个对象

首先,equals()方法不能作用于基本数据类型的变量,

另外,equals()方法存在于Object类中,而Object类是所有类的直接或间接父类,所以说所有类中的equals()方法都继承自Object类,在没有重写equals()方法的类中,调用equals()方法其实和使用==的效果一样,也是比较的是引用类型的变量所指向的对象的地址,不过,Java提供的类中,有些类都重写了equals()方法,重写后的equals()方法一般都是比较两个对象的值,比如String类。

总结:双等于比较的都是值,equals如果没重写和==相同,重写了按照重写后的逻辑。比如String比较的是字符串的值。equals不能用于基本数据类型。

Java valueOf() 方法

  • **Integer valueOf(int i):**返回一个表示指定的 int 值的 Integer 实例。
  • **Integer valueOf(String s):**返回保存指定的 String 的值的 Integer 对象。
  • Integer valueOf(String s, int radix): 返回一个 Integer 对象,该对象中保存了用第二个参数提供的基数进行解析时从指定的 String 中提取的值。

例子:Integer b = Integer.valueOf(“444”,16),输出是什么?

这段代码将字符串 "444" 解析为一个十六进制的整数。具体来说,它将字符串 "444" 解释为一个十六进制数字,然后转换为十进制整数。因为 "444" 是十六进制表示的数字,所以转换为十进制整数后的值为 1092。

Java基本类型和包装类型的区别

用途:包装类型应用的比基本类型更广泛,而且包装类型可以用于泛型。

当然基本类型存储占用空间较小,效率也较高。

包装类型的缓存机制

Java 基本数据类型的包装类型的大部分都用到了缓存机制来提升性能。简单点说就是已经把对象创建好了,不用额外创建,直接返回该对象即可。

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

两种浮点数类型的包装类 Float,Double 并没有实现缓存机制。

自动装箱与拆箱

有个例子讲的很好【每天一个Java知识】Java中的基本类型和包装类型_哔哩哔哩_bilibili

  • 装箱:将基本类型用它们对应的引用类型包装起来;
  • 拆箱:将包装类型转换为基本数据类型;
1
2
Integer i = 10;  //装箱
int n = i; //拆箱

intInteger 进行 == 比较时,会触发自动拆箱,将 Integer 对象转换为 int 值进行比较。

而当 intInteger 进行 equals() 方法比较时,会先将 int 值包装为 Integer 对象(自动装箱),然后再调用 Integer 类中的 equals() 方法来比较它们的值。

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

主要是生存时间、存储位置、语法形式的区别。局部变量不能被访问限定修饰符和 static 修饰。但是两个都可以被 final 修饰。

final的作用

第一种:用 final 来修饰数据。 final 是用来告诉编译器表示这块数据是恒定不变的。使用static加上final修饰的一个域,它是相当于占据一段不能改变的存储空间,必须赋初值。

第二种:用 final 修饰对象引用,那使用 final 修饰对象表示该对象的引用是恒定不变的,

第三种:空白final,当我们去定好一个空白final以后,就必须要保证在构造器构造的时候对它进行一个初始化。那假如我们没有在构造器中对它进行初始化,这个时候编译器就会报错了。 第四种:修饰参数,就是我们在定义方法的时候,可以改方法中的形参,用一个final来进行修饰,那么这种方法意味着无法在方法中再去更改参数的引用的指向的对象。

第五种:用 final 来修饰方法体,但使用 final 来修饰的方法我认为有两个原因,第一个相当于是把方法锁定,也就是说可以去防止任何基层内去修改它的一个含义,想要确保在基层以后使方法的行为保持不变,并且也不会去覆盖,那就使用翻了来修饰方法。 第六种:用 final 来修饰类,那么将某个类被定义为 final 的时候,就表示这个类不能被继承,也不允许做任何扩展,也不允许有任何的修改。

重载和重写的区别

A(什么都不会的我):重写就是把原有的方法重新改写,覆盖原有方法。重载不覆盖原有方法,但是可以根据参数,选择相同名称但是不同参数的方法。

A(改进版):重载就是同样的一个方法能够根据输入数据的不同,做出不同的处理。重写就是当子类继承自父类的相同方法,输入数据一样,但要做出有别于父类的响应时,你就要覆盖父类方法。【重写就是子类对父类方法的重新改造,外部样子不能改变,内部逻辑可以改变。

重写发生在运行期,是子类对父类的允许访问的方法的实现过程进行重新编写。

  1. 方法名、参数列表必须相同,子类方法返回值类型应比父类方法返回值类型更小或相等,抛出的异常范围小于等于父类,访问修饰符范围大于等于父类。
  2. 如果父类方法访问修饰符为 private/final/static 则子类就不能重写该方法,但是被 static 修饰的方法能够被再次声明。
  3. 构造方法无法被重写

什么是可变长参数

所谓可变长参数就是允许在调用方法时传入不定长度的参数。

1
2
3
public static void method1(String... args) {
//......
}

遇到方法重载的情况怎么办呢?会优先匹配固定参数还是可变参数的方法呢?

答案是会优先匹配固定参数的方法,因为固定参数的方法匹配度更高。

关于构造方法

首先构造方法不能被重写,因为子类无法继承父类的构造方法。

另外子类在进行构造的时候会默认调用父类的构造方法,隐式添加一条super()函数。

动态绑定机制

当调用对象方法的时候,会先从当前调用的子类向上找。

当调用属性的时候就是就近原则。

关于多态

编译时多态:也就是编译期间决定目标方法,特点是方法名相同,参数不同。一般采用overloading的方法。

运行时多态:运行期间决定目标方法,特点是同名同参,在继承的时候通过overriding实现,由JVM决定目标方法。

Q:底层机制是什么?

接口和抽象类

接口就是一种规范,抽象类就是里面有抽象方法的类。

接口里的方法不用加限定符,默认是public abstract的。

java 8开始引入了默认方法和静态方法,但是默认依旧是public修饰的。

Java 9 引入了私有方法(Private Methods)支持,允许接口中的默认方法和静态方法使用 private 关键字来定义私有方法,但这些私有方法只能在接口内部使用,不能被实现类或其他类调用。

深拷贝、浅拷贝和引用拷贝

引用拷贝就是只复制了引用,导致两个变量指向同一个对象。

浅拷贝就是虽然新创建了一个对象,但是对象中的引用依然指向同一个地址。

深拷贝就是完完全全复制另外一份。

【每天一个技术点】引用拷贝、浅拷贝、深拷贝_哔哩哔哩_bilibili

HashCode

其实和数据结构里一样的,HashCode主要是为了比较快速地找到索引位置,是为了效率,但是HashCode可能会冲突,这时候就需要重写equals方法。

为什么要有字符串常量池

为了节省资源,另外这个字符串常量池在堆里。

String对象的不可变性

String对象本质是char []数组(Java9之后变成了byte[]),然后这个char数组还被final和private修饰了,所以不可改变。为什么要设计成不可变呢?有三个原因,第一个是只有不可变才可以使用字符串常量池;第二个就是hashcode只需要计算一次,后续使用hashmap、hashset的时候更方便;第三个就是线程安全。

但是也有缺点,就是大量操作字符串时,会产生很多新对象。

String、String Buffer和String Builder的区别

StringBuffer是JDK1.0就推出的,是线程安全的(加了syncronized)但是效率相对较低。后来JDK1.5才推出了StringBuilder,线程不安全(没加final和private),但是相对高效一些。

关于+号的操作符重载,对于String类型,底层实际上是创建了一个StringBuilder类,然后调用其append方法,最后再toSting()转为String。