首页 > 2022届秋招Java后端高频知识点汇总①--Java基础
头像
悟空GGG
发布于 2021-12-09 14:36
+ 关注

2022届秋招Java后端高频知识点汇总①--Java基础

1. 重载和重写的区别

重载:重载就是同一个类中多个同名方法根据不同的传参来执行不同的逻辑处理。方法名必须相同,参数类型不同、个数不同、顺序不同。

返回值类型可以相同也可以不同。(因为返回值类型不是方法签名的一部分)

重写:重写是子类对父类的方法的实现过程进行重新编写。方法名,参数列表和返回值类型都不能改变。

抛出的异常范围小于等于父类,访问修饰符范围大于等于父类。


构造器是否可以被重写,是否可以被重载?

答:构造器不能被重写,可以被重载。


静态方法不能被重写,可以被重载静态方法可以被继承。

静态方法是类在加载时就被加载到内存中的方法,在整个运行过程中保持不变,因而不能重写。

但非静态方法是在对象实例化时才单独申请内存空间,为每一个实例分配独立的运行内存,因而可以重写。


在Java中,如果父类中含有一个静态方法,且在子类中也含有一个返回类型、方法名、参数列表均与之相同的静态方法,那么该子类实际上只是将父类中的该同名方法进行了隐藏,而非重写。

换句话说,父类和子类中含有的其实是两个没有关系的方法,它们的行为也并不具有多态性。


2. Java面向对象的三大特性

封装:封装把一个对象的属性私有化,不允许外部对象直接访问这些私有属性。同时提供一些可以被外界访问的属性的方法。

继承:继承是子类继承父类的非私有属性和方法。子类可以对父类的方法进行重写,也可以进行扩展,拥有自己的属性和方法。一个子类只能拥有一个父类,但是可以通过实现多个接口来达到多重继承的目的。

多态:多态表示当同一个操作作用在不同对象时,会有不同的结果。在Java语言中,多态主要有两种表现形式,方法的重载和重写。

多态分为编译时多态和运行时多态:重载(编译时多态)、重写(运行时多态)

1.方法重载:在同一个类中有多个同名的方法,但这些方法有着不同的参数,根据不同的传参可以进行不同的处理,因此在编译时就可以确定到底调用哪个方法,它是一种编译时多态。

2.方法重写:子类可以覆盖父类的方法,因此同样的方***在父类与子类中有着不同的表现形式。在Java语言中,父类的引用变量不仅可以指向父类的实例对象,也可以指向子类的实例对象。而程序调用的方法在运行时才动态绑定,就是引用变量所指向的具体实例对象的方法,也就是内存中正在运行的那个对象的方法,而不是引用变量的类型中的定义的方法。这就会出现相同类型的变量调用同一个方法时呈现出多种不同的行为特征,这就是多态。

这种在运行时才能确定调用哪个方法,被称为运行时多态。

使用多态的好处?

多态:同一操作作用于不同的对象,可以有不同的解释,产生不同的执行结果,这就是多态性。简单的说:就是用父类的引用指向子类的对象。

使用多态,可以解决代码的紧耦合的问题,提高程序的可扩展性。
1. 应用程序不必为每一个子类编写功能调用,只需要对抽象父类进行处理即可。大大提高程序的可复用性。

2. 子类的功能可以被父类的方法或引用变量所调用,这叫向上兼容,可以提高可扩充性和可维护性。


3. Java面向对象的5大设计原则

①单一职责原则:一个类只负责一个功能的实现

②里氏替换原则:只要父类出现的地方,都可以用子类替换

③依赖倒置原则:高层模块不应该依赖低层模块,二者都应该依赖其抽象。就是面向接口编程。

④接口隔离原则:接口的功能尽可能单一。接口更可能细化,不要建立臃肿庞大的接口。

⑤开闭原则:尽量通过扩展来面对需求的更改或者系统的变化,尽量不要对原有内容修改。


4. String、StringBuilder和StringBuffer的区别是什么?

①String是不可变的,StringBuilder和StringBuffer是可变的

String是不可变的,String类中使用final关键字修饰char字符数组来保存字符串,private final char value[],从Java9开始,String类的实现改用byte字节数组存储字符串private final byte[] value

而StringBuilder与StringBuffer都继承自AbstractStringBuilder类,在AbstractStringBuilder中也是使用字符数组保存字符串char[] value但是没有用final关键字修饰,所以这两种对象都是可变的。

②String和StringBuffer是线程安全的,StringBuilder不是线程安全的。

String中的对象的不可变的,所以是线程安全。

StringBuffer对方法加了synchronized同步锁,所以是线程安全的。

StringBuilder没有对方法加同步锁,所以不是线程安全的。

③执行效率方法:StringBuilder最高,StringBuffer次之,String最低。

每次对String类型进行改变的时候,都会生成一个新的String对象,然后将指针指向新的String对象。StringBuffer每次都会对StringBuffer本身进行操作,而不是生成新的对象并改变对象引用。相同情况下使用StringBuilder相比使用StringBuffer仅能获得10%~15%左右的性能提升,但要冒多线程不安全的风险。

对于三者使用的总结:

当操作少量数据时,优先使用String。

当在单线程下操作大量数据,优先使用StringBuilder类

当在多线程下操作大量数据,优先使用StringBuffer类


5. String为什么要设置成不可变的?

可以利用反射修改String 

1. 实现字符串常量池

字符串常量池(String pool, String intern pool, String保留池) 是Java堆内存中一个特殊的存储区域, 当创建一个String对象时,假如此字符串值已经存在于常量池中,则不会创建一个新的对象,而是引用已经存在的对象。假若字符串对象允许改变,那么将会导致各种逻辑错误,比如改变一个对象会影响到另一个独立对象. 严格来说,这种常量池的思想,是一种优化手段.

2. 允许String对象缓存HashCode

Java中String对象的哈希码被频繁地使用, 比如在HashMap 等容器中。字符串不变性保证了hash码的唯一性,因此可以放心地进行缓存.这也是一种性能优化手段,意味着不必每次都去计算新的哈希码.

3. 安全性

String被许多的Java类(库)用来当做参数,例如 网络连接地址URL,文件路径path,还有反射机制所需要的String参数等, 假若String不是固定不变的,将会引起各种安全隐患。

数据库的用户名、密码都是以字符串的形式传入来获得数据库的连接,或者在socket编程中,主机名和端口都是以字符串的形式传入。

因为字符串是不可变的,所以它的值是不可改变的,否则黑客们可以钻到空子,改变字符串指向的对象的值,造成安全漏洞。

在并发场景下,多个线程同时读写资源时,由于 String 是不可变的,不会引发线程的问题而保证了线程安全

总体来说, String不可变的原因包括 设计考虑,效率优化问题,以及安全性这三大方面. 事实上,这也是Java面试中的许多 "为什么" 的答案。

6. 自动装箱与拆箱

装箱:将基本类型用它们对应的引用类型包装起来。

拆箱:将包装类型转化为基本数据类型。

在装箱的时候自动调用的是Integer的valueOf(int)方法。而在拆箱的时候自动调用的是Integer的intValue方法。

Integer i = 100实际上是Integer.valueOf(100), int n = i 实际上是 i.intValue()


Integer i = 100实际上是Integer.valueOf(100),在valueOf()方法中,如果在[-128,127]之间的数,将直接从IntegerCache中获取。

IntegerCache中缓存了[-128,127]之间的值。

在通过valueOf方法创建Integer对象的时候,如果数值在[-128,127]之间,便返回指向IntegerCache.cache中已经存在的对象的引用;否则创建一个新的Integer对象。

7. 抽象和接口的异同?

不同点:

在JDK1.8之前接口只有方法的定义,不能有方法的实现。

JDK1.8中可以有默认方法(default修饰)和静态方法(static修饰)的实现。

JDK1.9开始接口中可以定义和实现私有方法(普通私有方法和静态私有方法)。

而抽象类可以有方法的定义与实现。

②接口里只能定义静态常量(static final),不能定义普通成员变量。抽象类中既可以定义静态常量,也能定义普通成员变量。

③接口中不能包含静态代码块,抽象类中可以包含静态代码块。

④一个类只能继承一个抽象类,但是一个类可以实现多个接口。

⑤接口强调的是特定功能的实现,抽象类强调的是所属关系。

⑥main 方法:接口不能有 main 方法;抽象类可以有 main 方法,并且我们能运行它;

⑦接口中定义的成员变量,只能是静态常量,默认修饰符为public static final,而且必须给其赋初值。

接口中定义的成员方法,抽象方法,默认修饰符为public abstract的。

抽象类中成员变量默认default(默认,什么也不写,同一包中可见),可在子类中被重新定义,也可被重新赋值。

抽象中抽象方法被abstract修饰,不能被private、static、synchronzed和native等修饰,必须以分号结尾,不带花括号。链接

⑧接口中不包含构造器;抽象类里可以包含构造器,抽象类中的构造器并不是用于创建对象,而是让其子类调用这些构造器来完成属于抽象类的初始化操作。

⑨接口被用于常用的功能,便于日后维护和添加删除。

抽象类更倾向于充当公共类的角色,不适用于日后重新对立面的代码修改。

功能需要累积时用抽象类,不需要累积时用接口。

相同点:

①接口和抽象类都不能被实例化。接口的实现类或抽象类的子类都只有实现了接口或抽象类中的方法后才能被实例化。

②接口和抽象类都可以包含抽象方法


8. Java中"=="与equals的区别?

"==":对于基本数据类型来说,则直接对值进行比较。如果是引用数据类型,则是对他们的地址进行比较。(因为Java只有值传递,所以对于==来说,不管是比较基本数据类型,还是引用数据类型的变量,其本质比较的都是值,只是引用类型变量存的值是对象的地址)

equals:equals是Object类提供的方法之一。每一个Java类都继承自Object类,所以每一个对象都具有equals方法。Object中的equals方法是直接使用"=="运算符比较的两个对象,所以在没有重写equals方法的情况下,equals与"=="运算符一样,比较的是地址。

可以通过重写equals方法来比较两个对象的内容是否相等。


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

如果两个对象equals()方法相等则它们的hashCode返回值一定要相同,如果两个对象的hashCode返回值相同,但它们的equals()方法不一定相等。

两个对象的hashCode()返回值相等不能判断这两个对象是相等的,但两个对象的hashcode()返回值不相等则可以判定两个对象一定不相等。

如果两个对象相等,则hashcode一定也是相同的。但是两个对象有相同的hashCode值,他们也不一定是相等的。

如果只重写equals方法而不重写hashCode方***造成hashCode的值不同,而equals方法判断出的结果是true。

因此,equals方法被覆盖过,则hashCode方法也必须被覆盖。

①如果重写了equals方法,而没有重写hashCode方***出现equals相等的对象,hashCode不相等的情况,重写hashCode方法是为了避免这种情况。

②使用hashCode方法提前校验,避免每一次比较都调用equals方法,提高效率。

面试官:你有没有重写过equals和hashcode?

答:在使用HashMap的“key”的部分存放自定义的对象时,重写过hashCode和equals方法。保证key是唯一的。


10. Java中的基本数据类型?

Java***有8中基本数据类型,其中包括

6种数字类型:byte、short、int、long、float、double

1种字符类型:char

1种布尔型:boolean

对于boolean型,官方文档未明确定义,它依赖于JVM厂商的具体实现。


11. final关键字

final用来修饰类、方法和变量

1.final修饰的类不能被继承,final类中的所有成员方法都会被隐式的指定为final方法。

(但是final修饰的类中成员变量是可变的,如果想要final类的成员变量不可变,必须给成员变量增加final修饰)

2.final修饰的方法不能被重写

3.final修饰的变量是常量。如果是基本数据类型的变量,则其数值一旦在初始化之后便不能更改。如果是引用类型的变量,则在对其初始化之后便不能让其指向另一个对象。


12. static关键字

static关键字主要有以下4种用法:

1.修饰成员变量和成员方法

被 static 修饰的成员属于类,不属于单个这个类的某个对象,被类中所有对象共享,通过类名调用。

被 static 声明的成员变量属于静态成员变量,静态变量 存放在 Java 内存区域的方法区

2.静态代码块

静态代码块定义在类中方法外, 静态代码块在非静态代码块之前执行(静态代码块—>非静态代码块—>构造方法)。 该类不管创建多少对象,静态代码块只执行一次。

3.静态内部类

static修饰类的话只能修饰内部类。静态内部类与非静态内部类之间存在一个最大的区别: 非静态内部类在编译完成之后会隐含地保存着一个引用,该引用是指向创建它的外围类,但是静态内部类却没有。没有这个引用就意味着:1. 它的创建是不需要依赖外围类的创建。2. 它不能使用任何外围类的非static成员变量和方法。

4.静态导包

用来导入类中的静态资源,1.5之后的新特性。 可以指定导入某个类中的指定静态资源,并且不需要使用类名调用类中的静态成员,可以直接使用类中静态成员变量和成员方法。


①父类静态代码块>>②子类静态代码块>>③父类非静态代码块>>④父类构造函数>>⑤子类非静态代码块>>⑥子类构造函数

13. 深拷贝和浅拷贝

浅拷贝:对基本数据类型进行值传递。对引用数据类型只是进行引用的传递,而没有真实的创建一个新的对象,此为浅拷贝。

深拷贝:对基本数据类型进行值传递,对引用数据类型,创建一个新的对象,并复制其内容,此为深拷贝。


14. Java异常体系

在 Java 中,所有的异常都有⼀个共同的祖先 java.lang 包中的 Throwable 类。Throwable: 有两个重要的⼦类:Exception(异常) 和 Error(错误) ,⼆者都是 Java 异常处理的重要⼦类,各⾃都包含⼤量⼦类。


Error(错误):是程序⽆法处理的错误,表示运⾏应⽤程序中较严重问题。⼤多数错误与代码编写者执⾏的操作⽆关,⽽表示代码运⾏时 JVM(Java 虚拟机)出现的问题。例如,Java 虚拟机运⾏错误(Virtual MachineError),当 JVM 不再有继续执⾏操作所需的内存资源时,将出现OutOfMemoryError。这些异常发⽣时,Java 虚拟机(JVM)⼀般会选择线程终⽌。

Exception(异常):是程序本身可以处理的异常。Exception 类有⼀个重要的⼦类RuntimeException。RuntimeException 异常由 Java 虚拟机抛出。NullPointerException(要访问的变量没有引⽤任何对象时,抛出该异常)ArithmeticException(算术运算异常,⼀个整数除以 0时,抛出该异常)和 ArrayIndexOutOfBoundsException (下标越界异常)。


15. 反射

1.什么是Java的反射

反射是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取信息以及动态调用对象的方法的功能称为 Java 语言的反射机制。

2.反射的作用

①运行时判断对象所属的类;

②运行时构造一个对象所属的类;

③运行时判断一个对象的方法和属性;

④运行时调用对象的方法和属性;

16. Java泛型

Java的泛型即“参数化类型”,允许程序在创建集合时指定集合元素的类型,表明该集合只能保存该类型的对象。

如果不使用泛型,当把一个对象存入集合后,集合就会忘记这个对象的数据类型,当再次取出该对象时,该对象的编译类型就变成了Object类型,还需要进行强制转换。

当使用泛型后,集合中是能存入集合指定类型的对象,否则将报错。并且将对象从集合取出后无需对元素进行强制转换,就是原本的类型。

在集合中存储对象并在使用前进行类型转换是多么的不方便。泛型防止了那种情况的发生。它提供了编译期的类型安全,确保你只能把正确类型的对象放入集合中,避免了在运行时出现ClassCastException。

17. 什么是泛型擦除

在代码中定义List<Object>和List<String>等类型,在编译后都会变成List,JVM看到的只是List,而由泛型附加的类型信息对JVM是看不到的。

在这个例子中,我们定义了两个ArrayList数组。

一个是ArrayList<String>泛型类型的,只能存储字符串;一个是ArrayList<Integer>泛型类型的,只能存储整数。

最后,我们通过list1对象和list2对象的getClass()方法获取他们的类的信息,最后发现结果为true。

说明泛型类型String和Integer都被擦除掉了,只剩下原始类型。

import java.util.*;
public class Test {
    public static void main(String[] args) {
        List<String> l1 = new ArrayList<String>();
        List<Integer> l2 = new ArrayList<Integer>();

        System.out.println(l1.getClass() == l2.getClass());  //true

        //l1.getClass():java.util.ArrayList
        System.out.println("l1.getClass():"+l1.getClass().getName());
        //l1.getClass():java.util.ArrayList
        System.out.println("l1.getClass():"+l2.getClass().getName());
    }
}


18. Java Object类中的方法

getClass() 返回一个对象的运行时类。 

int hashCode() 返回该对象的哈希码值。 

boolean equals(Object obj) 指示某个其他对象是否与此对象“相等”。 

String toString() 返回该对象的字符串表示。 

void notify() 唤醒在此对象监视器上等待的单个线程。 

void notifyAll() 唤醒在此对象监视器上等待的所有线程。 

void wait() 导致当前的线程等待,直到其他线程调用此对象的 notify() 方法或 notifyAll() 方法。 

protected Object clone() 创建并返回此对象的一个副本。 

protected void finalize() 当垃圾回收器确定不存在对该对象的更多引用时,由对象的垃圾回收器调用此方法。 

19. Java String类中的方法


20. Java创建对象的5种方式?

①使用new关键字

②使用Class类的newInstance方法

③使用Constructor类的newInstance方法

④使用clone方法

⑤使用反序列化


21. Java访问修饰符的范围


22. Hash冲突的解决方式?

1.开放定址法

按照形成探查序列的方法不同,可将开放定址法区分为线性探查法、二次探查法、随机探查法。

①线性探查法

②二次探查法

③随机探查法

2.拉链法

将所有哈希地址为i的元素构成一个称为同义词链的单链表,并将单链表的头指针存在哈希表的第i个单元中,因而查找、插入和删除主要在同义词链中进行。链地址法适用于经常进行插入和删除的情况。

3.再Hash法

当计算出的hash值产生冲突时,再计算另一个Hash函数的哈希值,直到冲突不再发生为止。

4.建立公共溢出区

将哈希表分为基本表和溢出表两部分,凡是和基本表发生冲突的元素,一律填入溢出表。

23. Java中的流分为几种?

按照流的流向分,可以分为输⼊流和输出流;

按照操作单元划分,可以划分为字节流和字符流;

按照流的⻆⾊划分为节点流和处理流。

Java Io 流共涉及 40 多个类,这些类看上去很杂乱,但实际上很有规则,⽽且彼此之间存在⾮常紧密的联系,

Java I0 流的 40 多个类都是从如下 4 个抽象类基类中派⽣出来的。

InputStream/Reader: 所有的输⼊流的基类,前者是字节输⼊流,后者是字符输⼊流。

OutputStream/Writer: 所有输出流的基类,前者是字节输出流,后者是字符输出流






全部评论

(9) 回帖
加载中...
话题 回帖

近期热帖

热门推荐