Java面试—基础篇

文章目录

1、面向对象的三大特性?

封装:隐藏部分对象的属性和细节,对数据的访问只能通过对外公开的接口,对象内部的数据提供了保护

继承:继承就是子类继承父类的特征和行为,使得子类对象(实例)具有父类的实例域和方法,或子类从父类继承方法,使得子类具有父类相同的行为

多态:对于同一个行为,不同的子类对象具有不同的表现形式。多态存在的三个条件:1.继承 2.重写 3.父类引用指向子类方法。通俗点来说,就像打游戏,同一个按键,不同的角色会释放不同的技能

2、Java有什么耳熟能详的特点?

  1. 简单易学:Java丢弃了C++中很难理解的运算符重载、多重继承等模糊概念。特别是Java语言不使用指针,而是使用引用,并提供了自动的垃圾回收机制,使程序员不必过多的操心内存管理的问题。

  2. 面向对象:记住封装、继承、多态这三大特点。

  3. 平台无关性:一次编译到处运行(Write Once,Run any Where),原因就是有Java虚拟机(JVM)的支持,.java文件编译成.class,然后通过JVM将.class翻译成特定平台的机器码进行运行。

  4. 支持多线程:Java语言内置了多线程控制,可使用户程序并发执行。利用Java的多线程编程接口,开发人员可以方便地写出多线程的应用程序,提高程序的执行效率。

  5. 其他:安全可靠、编译与解释共存、支持网络编程等。

3、JVM、JDK 和 JRE 有什么区别?

  1. JVM:Java虚拟机,Java程序运行在Java虚拟机上。针对不同的系统实现不同的JVM,因此Java语言可以实现跨平台。

  2. JRE:Java运行时环境,它是运行已编译Java程序所需的所有内容的集合,包括JVM,Java类库,Java命令和其他的一些东西。

  3. JDK:Java开发工具包,包括了JRE。

三者关系

4、什么是字节码?字节码的好处是什么?

  • JVM可以理解的代码就是字节码(.class)

  • Java程序经过编译产生的.class,字节码能够被虚拟机识别,不同的平台安装不同的JVM,从而实现Java程序的跨平台性。

Java程序运行的三步:

  1. **编译:**将.java编译成为.class
  2. **解释:**JVM将.class解释为机器码
  3. **执行:**机器码执行

5、为什么说Java语言“编译与解释共存”?

首先理解一下概念,高级编程语言按照程序的执行方式分为两种:

  • 编译型编译型语言会通过编译器源码一次性的翻译成可被该平台执行的机器码。例如C、C++、GO、Rust等。
    • 优点:执行速度较快(所以说那些算法竞赛,都是C++为主)。
    • 缺点:开发效率低。
  • 解释型解释型语言会通过解释器一句一句的将代码解释为机器代码。例如Python、JavaScript、PHP等。
    • 优点:开发效率快。
    • **缺点:**执行速度慢。

前面的几点如果都看了的话,应该能大致理解Java语言编译与解释共存是什么意思了吧!Java程序执行先要编译,后要解释,介于这两种概念的之间,拥有两种高级语言的特征。

6、Java基本数据类型?

八大基础数据类型:

基本类型 位数 字节 **默认值 ** 取值范围
byte 8 1 0 -128~127
short 16 2 0 -32768~32767
int 32 4 0 -2147483648 ~ 2147483647
long 64 8 0L -9223372036854775808 ~ 9223372036854775807
char 16 2 ‘u0000’ 0 ~ 65535
float 32 4 0f 1.4E-45 ~ 3.4028235E38
double 64 8 0d 4.9E-324 ~ 1.7976931348623157E308
boolean 1 false true、false

对于boolean来说,不同的JVM厂商不同的实现。

引用数据类型:
类、接口、数组

7、自动类型转换、强制类型转换?

自动类型转换方向
自动类型转换:就是低精度高精度转换(最多后面加点.0000的精度,不会造成精度丢失)。

强制类型转换:就是高精度低精度转换(比如是1.999变成低精度的,就变成了1,本来差不多2,你说说这能行吗)。

8、自动装箱与拆箱了解吗?原理是什么?

自动装箱与拆箱:

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

原理
装箱其实就是调用了 包装类的valueOf()方法,拆箱其实就是调用了 xxxValue()方法。

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

  1. 一个类可以实现多个接口,但是只能实现一个抽象类。接口自己可以通过extends关键字扩展多个接口。

  2. 接口方法默认修饰符是public,抽象方法可以有public、protected和default这些修饰符。

  3. 接口中所有的方法都不能有实现(jdk8开始接口方法可以有默认实现),抽象类可以有非抽象的方法。

  4. 从设计层面来说,抽象是对类的抽象,是⼀种模板设计,而接⼝是对行为的抽象,是⼀种行为的规范。

  5. 接口中除了 static 、 final 变量,不能有其他变量,而抽象类不一定。

10、String,StringBuilder和StringBuffer的区别?

  • StringString 的值被创建后不能修改,任何对 String 的修改都会引发新的 String 对象的生成。

  • StringBuffer跟 String 类似,但是值可以被修改,不会引起新的StringBuffer对象生成,使用 synchronized 来保证线程安全,适用于多线程。

  • StringBuilder和StringBuffer差不多,但是线程不安全,适用于单线程,性能会高一些。

11、Switch的case都能支持什么数据类型?

Java5以前只能支持byte、short、char、int。Java5之后支持enum类型。从Java7开始,支持String,但是long在所有版本中都不适用。

12、字符型常量和字符串常量的区别?

** ** ** 形式** 含义 占内存大小
字符常量 由单引号引起的字符 字符相当于一个整型值,可以参加表达式运算 2个字节
字符串常量 由双引号引起的0个或多个字符 字符串代表一个地址 若干个字节

13、看看下面几个自增运算的值?

int i  = 1;
i = i++;
System.out.println(i);

答案为1

int count = 0;
for(int i = 0;i < 100;i++)
{
    
    
    count = count++;
}
System.out.println("count = "+count);

答案为0

是不是和你想的不太一样,这里是当进行自增运算的时候,就会又一个临时变量去存储去接受i或count的值,然后再把临时变量去赋值给i或count,最后就是以上的结果。

14、静态方法(变量)和实例方法(变量)有何不同?

** ** ** 静态方法** 实例方法
调用方式 通常使用**类名.方法名**的方式(调用静态方法无需创建对象,经常在工具类中见) 必须先创建对象再去用,**对象.方法名**的方式去调用
访问类成员是否限制 静态方法在访问本类的成员时,只允许访问静态成员(静态变量和静态方法) 随便访问
** 静态变量** ** 实例变量**
被static修饰,它属于类,但是不属于类的任何一个对象,一个类不管创建多少个对象,静态变量在内存中有且仅有一个副本 需要先创建对象然后通过对象才能访问到它,每一个对象都是一个独立的副本

15、重载(overload)和重写(override)的区别?

  • 方法的重载和重写都是实现多态的方式,区别在于前者实现的是编译时的多态性,而后者实现的是运行时的多态性。
方法重载 ** 发生在同一个类中,方法名一致,参数类型顺序、类型、个数不同,可以抛出不同的异常,可以有不同的修饰符**
方法重写 发生在子类和父类中,要求父类被重写的方法有相同的返回类型,不能比父类被重写方法声明更多的异常

16、面向对象和和面向过程的区别?

  • 面向对象(Object Oriented: OO):先抽象出对象,然后用对象执行方法的方式解决问题。

  • 面向过程(Procedure Oriented : OP):把解决问题的过程拆成一个个方法,通过一个个方法的执行解决问题

17、深拷贝和浅拷贝的区别?

image.png

18、this关键词有什么作用?

  1. 普通的直接引用,指向对象本身的一个东西,相对于就是对象本身。

  2. 当形参和实参名字重复,用this区分

  3. 访问构造方法

19、final 关键字有什么作用?

final表示不可变的意思,可用于修饰类、属性和方法。

  1. 被final修饰的类不可以被继承

  2. 被final修饰的方法不可以被重写(就和绝育一样)

  3. 被final修饰的变量不可变,还必须要初始化值,这里不可变指的是变量的引用不可变,而不是引用的内容不可变

20、final、finally、finalize三个关键字的区别?

  • final看上面

  • finally经常与try/catch一起使用,它的语句块是无论如何都会去被执行的。

这上面说的是一般情况,但是遇到下面这三种情况的话,不一定会执行的:

  1. finally之前虚拟机被终止
  2. 程序所在的线程死亡
  3. 关闭CPU
  • finalize 是在java.lang.Object里定义的方法,也就是说每一个对象都有这么一个方法,这个方法在gc启动,该对象被回收的时候调用。

一个对象的 finalize 方法只会被调用一次,finalize 被调用不一定会立即回收该对象,所以有可能调用 finalize 后,该对象又不需要被回收了,然后到了真正要被回收的时候,因为前面调用过一次,所以不会再次调用 finalize 了,进而产生问题,因此不推荐使用 finalize 方法

21、Java到底是值传递还是引用传递?

Java是值传递的。先了解一下基本类型和引用类型。
image.png

public class test {
    
    
    public static void main(String[] args) {
    
    
        int num = 10;
        System.out.println(num); // 10
        jia(num);
        System.out.println(num); // 10
    }
    public static void jia(int num1) {
    
    
        num1++;
    }
}

public class test {
    
    
    public static void main(String[] args) {
    
    
        String str = "hello";
        System.out.println(str); // hello
        jia(str);
        System.out.println(str); // hello
    }
    public static void jia(String str1) {
    
    
        str1 = "world";
    }
}

第一组代码,当我们调用方法jia的时候传过去的num,经过num1++,main中输出的num没有发生变化。
第二组代码,和上面那个差不多,当我们str1=“world”,也就是str1指向的地方变了,不影响str指向的"hello"。
这里可以证明num和num1不是一个东西,str和str1不是同一个引用,我们才叫这东西叫值传递。

image.png

public class test {
    
    
    /**
     * public class A {
     *    String str = "hello";
     * }
     */
    public static void main(String[] args) {
    
    
        A a = new A();
        System.out.println(a.str); // hello
        jia(a);
        System.out.println(a.str); // world
    }
    public static void jia(A str1) {
    
    
       str1.str = "world";
    }
}

不是说是值传递吗,怎么str又发生了变化了,这里的jia方法修改的str实际上和a中的str指向的地方一样,但是整体来说还是值传递,这个a对象和a1对象是两个不一样的引用。

22、==和equals的区别?

==的使用:

  • 对于基本类型来说,**==**比较的是值。
  • 对于引用类型来说,**==**比较的是对象的内存地址。

equals的使用:

  • 类没有重写该方法,使用的时候等价于**==**
  • 类重写该方法,比较的是两个对象的内容是否相等

我们使用String的时候,比较的使用的就是equals,这就是因为String重写了equals和hashCode方法,去比较内容是否相同

23、为什么重写 equals() 时必须重写 hashCode() 方法?

equals方法判断两个对象是相等的,那么这两个对象的hashcode值也要相等。如果重写equals没有重写hashCode就可能导致equals判断是想的呢过的两个对象,但是他们的hashCode值不相等。

也就是说:

  • equals方法判断两个对象是相等的,那么这两个对象的hashcode值也要相等。
  • 两个对象有相同的hashcode值,他们不一定是相等的(哈希碰撞)。

24、String 是 Java 基本数据类型吗?可以被继承吗?

String不是Java的基本数据类型,基本数据类型只有那8种。

String类是被final修饰的,String 类中保存字符串的字符数组也是由final修饰的,不能被继承。

25、字符串拼接用“+” 还是 StringBuilder?

+的字节码来看的话,实际上也是通过StringBuilder的append方法实现的,+完后使用toString去得到一个String对象。

Jdk9以前,如果在循环中使用+的话,每次循环都会创建一个StringBuilder对象,直接使用StringBuilder不会这样,Jdk9以后这个重复创建StringBuilder对象的问题被修复。

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

  • 如果字符串常量池中不存在字符串对象“abc”的引用,那么会在堆中创建 2 个字符串对象“abc”:
String s1 = new String("abc");

会在堆中创建一个String对象,然后在常量池中创建“abc”字符串对象,然后给堆中的String对象初始化。

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

在堆中创建一个String对象,直接从常量池中的"abc"给其初始化。

27、intern 方法有什么作用?

String.intern()是一个native方法,作用是将指定的字符串对象的引用保存在字符串常量池中:

  • 如果当前字符串内容存在于字符串常量池,直接返回字符串常量池中的引用。
  • 否则,就将String对象添加到池中,并返回String对象的引用。

可以参考intern方法_wzdhc的博客-CSDN博客

28、Java的异常体系?受检查和非受检查异常区别?

  • Checked Exception 即 受检查异常:编译过程中,编译器会强制检查并要求处理的异常。
  • Unchecked Exception 即 不受检查异常,RuntimeException及其子类统称为非受检查异常:编译过程中,即使不处理检查异常也可以正常通过编译。
** **NullPointerException ** **空指针错误
IllegalArgumentException 参数错误比如方法入参类型错误
NumberFormatException 字符串转换为数字格式错误
ArrayIndexOutOfBoundsException 数组越界错误
ClassCastException 类型转换错误
ArithmeticException 算术错误
SecurityException 安全错误比如权限不够
UnsupportedOperationException 不支持的操作错误比如重复创建同一用户

常见异常

29、序列化和反序列化?序列化的方式?不想序列化怎么办?

  • 序列化:将数据结构或对象转换为二进制字节流的过程
  • 反序列化:将在序列化过程中所产生的二进制字节流转换成数据结构或者对象的过程

适用场景:持久化Java对象(Redis存储),在网络中传输Java对象(RPC远程调用),通俗来说,就是将一个东西拆开,然后运输,最后再组装起来

目前序列化的方式有三种:

  1. Java对象序列化Java原生序列化方法通过Java原生流InputStream和OutputStream方式进行转化
  2. JSON序列化这个是我见过最多的的序列化方式,常见的包有Jackson、FastJson
  3. ProtoBuff 序列化ProtocolBuffer 是一种轻便高效的结构化数据存储格式,ProtoBuff 序列化对象可以很大程度上将其压缩,可以大大减少数据传输大小,提高系统性能

如果对象中有些变量不想进行序列化,要使用transient关键字修饰。

  1. 阻止实例中使用了该关键字修饰的变量序列化,当对象被反序列化的时候,被其修饰的变量也不会恢复。
  2. 该关键字只能修饰变量,不能修饰类和方法。

30、什么是泛型?使用方式有哪些?常用通配符?

  • Java泛型是JDK5中引入的特性,使用泛型参数,可以增强代码的可读性和稳定性。

  • 编译器可以对泛型参数进行检测,并且通过泛型参数可以指定传入的对象类型, 比如 List lists = new ArrayList() 这行代码就指明了该 ArrayList 对象只能传入 Person 对象,如果传入其他类型的对象就会报错。

一般的使用方式有:泛型类、泛型接口、泛型方法

  1. 泛型类
//此处T可以随便写为任意标识,常见的如T、E、K、V等形式的参数常用于表示泛型
//在实例化泛型类时,必须指定T的具体类型
public class Generic<T>{
    
    

    private T key;

    public Generic(T key) {
    
    
        this.key = key;
    }

    public T getKey(){
    
    
        return key;
    }
}

Generic<Integer> genericInteger = new Generic<Integer>(123456);
  1. 泛型接口
public interface Generator<T> {
    
    
    public T method();
}

class GeneratorImpl<T> implements Generator<String>{
    
    
    @Override
    public String method() {
    
    
        return "hello";
    }
}
  1. 泛型方法
public static < E > void printArray( E[] inputArray )
{
    
    
     for ( E element : inputArray ){
    
    
        System.out.printf( "%s ", element );
     }
     System.out.println();
}

常用的通配符为: T,E,K,V,?

  • ? 表示不确定的 java 类型
  • T (type) 表示具体的一个 java 类型
  • K V (key value) 分别代表 java 键值中的 Key Value
  • E (element) 代表 Element

31、Java中IO流分为?

  • 按照流的流分向,可以分为输入流输出流
  • 按照操作单元划分,可以划分为字节流字符流
  • 按照流的角色来划分,分为节点流处理流

IO流用到了一个设计模式—装饰器模式

32、说一下对注解的理解?注解的解析方法有哪几种?

  • 注解是Java5后开始引入的新特性,可以看做是一种特殊的注释,主要用于修饰类、方法、变量等,提供某些信息供程序在编译或者运行时使用,注解本质是一个继承了Annotation 的特殊接口

  • 常见的注解解析方式有两种:

    • 编译期直接扫描:编译器在编译java代码的时候扫描对应的注解并处理。
    • 运行期通过反射处理:像框架(Spring、mybaits)中自带的注解都是通过反射来进行处理的

33、反射是什么?优缺点?应用场景?原理?

  • 反射是什么:我们想在动态的获取到类信息、创建类实例、调用类方法这时候就要使用到反射。通过反射可以获取到任意一个类的所有属性和方法,还可以调用这些方法和属性。

  • 反射的优缺点:

    • 优点:反射可以让我们的代码更加灵活,为各种框架提供开箱即用的功能的便利
    • 缺点:它增加了安全问题,可以无视泛型参数的安全检查;反射的性能也要差点
  • 应用场景:很多设计、开发都与反射机制有关,例如模块化的开发,通过反射去调用对应的字节码,用动态代理设计模式也采用了反射机制,例如下面:

    • JDBC数据库连接
    • Spring框架的使用
  • 原理:Java程序执行分为编译和运行两步,编译之后会生成字节码文件,JVM进行类加载的时候,会加载字节码文件,将类型相关的的所有信息都加载进方法区中,反射就是去获取这些信息,然后进行各种操作

34、Stream流用过吗?

Stream 流,简单来说,使用 java.util.Stream 对一个包含一个或多个元素的集合做各种操作。这些操作可能是 中间操作 亦或是 终端操作。 终端操作会返回一个结果,而中间操作会返回一个 Stream 流。

  • 过滤(Filter)
  • 排序(Sorted)
  • 转换(Map)
  • 匹配(Match)
  • 计数(Count)
  • 等等等等

image.png

35、jdk1.8新特性?

  1. 默认接口方法:允许给接口添加非抽象的方法,通过关键字default
  2. Lambda表达式和函数式接口:Lambda表达式本质上是一段匿名内部类,也可以是一段可以传递的代码。Lambda允许把函数作为一个方法的参数,使用Lambda表达式使代码更加简洁。
  3. Stream API:用函数式编程方式在集合类上进行复杂操作的工具,配合Lambda表达式可以方便对集合进行处理。
  4. 时间日期API:Java8引入了新的日期时间类。
  5. Optional 解决NullPointerException

猜你喜欢

转载自blog.csdn.net/weixin_52487106/article/details/130833310
今日推荐