0%

JavaSE

1. JVM、JDK 和 JRE 有什么区别

JVM是Java虚拟机,Java程序运行在JVM上。而正是因为JVM,使得Java具有了跨平台性。

JRE是Java运⾏时环境,包含在JDK内,它是运⾏已编译 Java 程序所需的所有内容的集合,包括 Java 虚拟机(JVM),Java 类库,Java 命令和其他的⼀些基础构件。

JDK提供了开发Java程序所需的一系列类包、环境等,它拥有 JRE 所拥有的⼀切,还有编译器(javac)和⼯具(如 javadoc 和 jdb)。它能够创建和编译程序。

2. 为什么说Java语言“编译与解释并存”?

高级编程语言按照程序的执行方式分为编译型解释型两种。

说Java是编译型是因为,Java语言是经过编译器编译成字节码后在JVM上运行的。

说Java是解释型是因为,字节码需要在JVM上经过解释成操作系统能识别的语言(机器码),再由操作系统去执行。

3. Java 有哪些数据类型?

Java的数据类型分为基本数据类型引用类型

基本数据类型有:int,long,float,byte,short,double,char,boolean

引用类型有:class、interface、数组

引申:为什么Java里有基本数据类型和引用数据类型?

     存储方式:引用类型在堆里而基本类型在栈里。栈空间小且连续,存取速度比较快;在堆中则需要new,对基本数据类型来说空间浪费率太高;
 
 传值方式:基本类型是在方法中定义的非全局基本数据类型变量,调用方法时作为参数是按数值传递的;引用数据类型变量,调用方法时作为参数是按引用地址传递的;

如果不声明,默认小数为double类型,所以如果要用float的话,必须进行强转

例如:float a=1.3; 会编译报错,正确的写法 float a = (float)1.3;或者float a = 1.3f;(f或F都可以不区分大小写)

4. 详解装箱和拆箱

5. 抽象类(abstract class)和接口(interface)有什么区别?

  1. 接口的方法默认是 public ,所有方法在接口中不能有实现(Java 8 开始接口方法可以有默认实现),而抽象类可以有非抽象的方法。
  2. ⼀个类可以实现多个接口,但只能实现⼀个抽象类。接口自己本身可以通过 extends 关键字扩展多个接口。
  3. 从设计层⾯来说,抽象是对类的抽象,是⼀种模板设计,而接口是对行为的抽象,是⼀种行为的规范。
  4. 抽象类和接口都不能实例化。

总结⼀下 jdk7~jdk9 Java 中接口的变化:

  1. 在 jdk 7 或更早版本中,接口里面只能有常量变量和抽象方法。这些接口方法必须由选择实现接口的类实现。
  2. jdk 8 的时候接口可以有默认方法和静态方法功能。
  3. jdk 9 在接口中引入了私有方法和私有静态方法。

6. final关键字的作用

final关键字表示不可变,可以修饰类、属性和方法。

被final修饰的类不可继承。

被final修饰的属性不可变,被 final 修饰的变量必须被显式第指定初始值,还得注意的是,这里的不可变指的是变量的引用不可变,不是引用指向的内容的不可变。

被final修饰的方法不可被重写。

1
2
3
final StringBuilder sb = new StringBuilder("abc");
sb.append("d");
System.out.println(sb); //abcd

上面的例子则说明了变量不可变的含义是指引用不可变。

7. 重写和重载的区别

重载

方法重载是让类以统一的方式处理不同类型数据的一种手段。多个同名函数同时存在,具有不同的参数个数/类型。重载Overloading是一个类中多态性的一种表现。

重写:

1)参数列表必须完全与被重写的方法相同,否则不能称其为重写而是重载。

2)返回的类型必须一直与被重写的方法的返回类型相同,否则不能称其为重写而是重载。

3)访问修饰符的限制一定要大于被重写方法的访问修饰符(public>protected>default>private)

4)重写方法一定不能抛出新的检查异常或者比被重写方法申明更加宽泛的检查型异常。例如:

父类的一个方法申明了一个检查异常IOException,在重写这个方法是就不能抛出Exception,只能抛出IOException的子类异常,可以抛出非检查异常。

1
2
3
4
5
6
7
8
9
10
11
public int doSomething() {
return 0;
}
// 输入参数不同,意味着方法签名不同,重载的体现
public int doSomething(List<String> strs) {
return 0;
}
// return类型不一样,编译不能通过
public short doSomething() {
return 0;
}

8. final、finally、finalize 的区别?

final:见第6点。

finally:是和try、catch结合使用的。finally包含的代码块,一定会被执行。

finalize 是基础类 java.lang.Object 的一个方法,它的设计目的是保证对象在被垃圾收集前完成特定资源的回收。finalize 机制现在已经不推荐使用,并且在 JDK 9 开始被标记为 deprecated。

9. ==和 equals 的区别?

对于基本数据类型,==比较的是他们的值,而基本数据类型没有equals方法。

对于引用类型,==比较的是他们的地址。如果没有对equals方法进行重写,则比较的是引用类型的变量所指向的对象的地址;

诸如String、Date等类对equals方法进行了重写的话,比较的是所指向的对象的内容。

10. 为什么重写 equals 时必须重写 hashCode 方法?

因为如果两个对象相等,那么他们的hash值一定相等,对两个对象分别调⽤ equals 方法都返回 true。反之如果两个对象hash值相等,他们不一定相等。因此,equals 方法被覆盖过,则 hashCode 方法也必须被覆盖。

hashCode() 的默认行为是对堆上的对象产生独特值。如果没有重写 hashCode() ,则该 class 的两个对象无论如何都不会相等(即使这两个对象指向相同的数据)。

11. Java 是值传递,还是引用传递?

Java 语言是值传递。JVM 的内存分为堆和栈,其中栈中存储了基本数据类型和引用数据类型实例的地址,也就是对象地址。

而对象所占的空间是在堆中开辟的,所以传递的时候可以理解为把变量存储的对象地址给传递过去,因此引用类型也是值传递。

12. Java如何实现浅拷贝和深拷贝?

浅拷贝:Object 类提供的 clone()方法可以非常简单地实现对象的浅拷贝。

深拷贝:

  • 重写克隆方法:重写克隆方法,引用类型变量单独克隆,这里可能会涉及多层递归。
  • 序列化:可以先将原对象序列化,再反序列化成拷贝对象。

13. Java创建对象有哪些方式?

  • new 创建新对象
  • 通过反射机制
  • 采用 clone 机制
  • 通过序列化机制

14. String 不是不可变类吗?字符串拼接是如何实现的?

jdk1.8 之前,a 和 b 初始化时位于字符串常量池,ab 拼接后的对象位于堆中。经过拼接新生成了 String 对象。如果拼接多次,那么会生成多个中间对象。

内存如下:

jdk1.8之前的字符串拼接

Java8 时JDK 对“+”号拼接进行了优化,上面所写的拼接方式会被优化为基于 StringBuilder 的 append 方法进行处理。Java 会在编译期对“+”号进行处理。

这样看,使用 + 号 和使用 StringBuilder 是没有区别的,在一般情况下是这样,但是在循环里面,建议使用 StringBuilder 。

举个例子,看一段这样的代码:

1
2
3
4
5
6
7
public static void main(String[] args) {
String s= "" ;
for(int i=1;i<10;i++){
s+=i;
}
System.out.println(s);
}
  • 使用jad工具反编译代码
1
2
3
4
5
6
7
public static void main(String args[]){
String s = "":
for(int i = 1; i < 10; i++) {
s = (new StringBuilder(String.valueOf(s))).append(i).tostring() ;
}
System.out.printin(s);
}

正确的做法应该是在循环外层先创建好StringBuilder 对象,然后在循环体内使用append 方法进行处理。

15. 什么是序列化?什么是反序列化?Serializable 接口有什么用?

序列化是将Java对象转换为二进制流,反序列化是将二进制流转换为对象。

这个接口只是一个标记,没有具体的作用,但是如果不实现这个接口,在诸如使用Dubbo等RPC调用场景的情况下,会抛出异常。

Dubbo RPC 方法类都要实现Serializable接口的原因

dubbo在使用hessian2协议序列化方式的时候,对象的序列化使用的是JavaSerializer

1
2
3
4
5
com.alibaba.com.caucho.hessian.io.SerializerFactory#getDefaultSerializer

com.alibaba.com.caucho.hessian.io.SerializerFactory#getSerializer

com.alibaba.com.caucho.hessian.io.Hessian2Output#writeObject

获取默认的序列化方式的时候,会判断该参数是否实现了Serializable接口

1
2
3
4
5
6
7
8
9
10
11
protected Serializer getDefaultSerializer(Class cl) {
if (_defaultSerializer != null)
return _defaultSerializer;
// 判断是否实现了Serializable接口
if (!Serializable.class.isAssignableFrom(cl)
&& !_isAllowNonSerializable) {
throw new IllegalStateException("Serialized class " + cl.getName() + " must implement java.io.Serializable");

}
return new JavaSerializer(cl, _loader);
}

16. Java 泛型了解么?什么是类型擦除?为什么要类型擦除?介绍一下常用的通配符?

Java泛型是JDK1.5的特性,泛型本质上是参数化类型,也就是说操作的数据类型被指定为一个参数。

类型擦除是指在编译时,所有的泛化类型被擦除,转换成实际需要的类型。

需要类型擦除主要是为了向下兼容,因为 JDK5 之前是没有泛型的,为了让 JVM 保持向下兼容,就出了类型擦除这个策略。

常见通配符有K、T、E、V。

17. 正向代理和反向代理的区别

网络代理分为正向代理和反向代理,代理其实就是一个中介,最初的时候,只有正向代理,是帮助内网客户端访问外网服务器的,后来出现了反向代理,是把外网客户端的请求转发给内网服务器。
其实最简单的区别,正向代理代理的是客户端,反向代理代理的是服务器。正向代理一般是客户端架设的,反向代理一般是服务器架设的。
1、代理对象不同。正向代理代理的是客户端,反向代理代理的是服务器。正向代理帮助客户访问其无法访问的服务器资源,反向代理帮助服务器做负载均衡,另外,由于客户端跟真实服务器不直接接触,能起到一定安全防护的作用。
2、架设主体不同。正向代理一般是客户端架设的,比如在自己的机器上装一个代理软件,反向代理一般是服务器架设的,通常是在机器集群中部署个反向代理服务器。
3、保护对象不同。正向代理保护对象是客户端,反向代理保护对象是原始资源服务器。
4、作用目的不同。正向代理主要目的是解决访问限制问题,而反向代理一方面是作为负载均衡,再就是起到安全防护的作用。

18. 在Java中,String为什么设置成不可变类型

在Java中,String被设计成不可变类型,是为了提高字符串的安全性和可靠性。具体来说,有以下几个原因:

  1. 线程安全:由于字符串是不可变的,所以多个线程可以同时访问同一个字符串对象,而不需要担心数据被篡改,从而提高了程序的线程安全性。
  2. 缓存哈希值:由于字符串的哈希值在创建时就被缓存起来了,所以不需要每次使用字符串时都重新计算哈希值,从而提高了程序的性能。
  3. 安全性:由于字符串是不可变的,所以在使用字符串时不需要担心数据被意外修改,从而提高了程序的安全性。
  4. 简化代码:由于字符串是不可变的,所以在使用字符串时可以省去一些代码,比如不需要手动处理字符串的长度、位置等信息,从而使代码更加简洁易懂。

欢迎关注我的其它发布渠道