零、前言
1、目的
帮助掌握面试题,就八股文相关内容展开进行学习和整理,也方便之后的复习和巩固。
2、八股文内容来源
①https://blog.csdn.net/w20001118/article/details/125724647
一、具体内容分析
1、类的完整书写方式
1.1、类
[Access Modifier] [Modifiers] [Class Name] [Inheritance] [Implemented Interfaces] [Class Body]
具体实例:
public class MyClass extends ParentClass implements Interface1, Interface2 {
// 成员变量
private int myField;
// 构造函数
public MyClass() {
// 构造函数的实现
}
// 方法
public void myMethod() {
// 方法的实现
}
// 实现接口中的方法
@Override
public void interfaceMethod() {
// 方法的实现
}
}
详解:
①访问修饰符(Access Modifier):用于控制类、成员变量和方法的访问级别,决定了它们可以被哪些其他代码访问
public
:被声明为 public
的类、成员变量和方法可以被任何其他类的代码访问
protected
:被声明为 protected
的成员变量和方法只能被同一个包中的其他类或其子类访问
private
:被声明为 private
的成员变量和方法只能被同一个类中的其他方法访问,不能被其他类的代码访问
默认(缺省)
:被声明为默认访问级别的类、成员变量和方法只能被同一个包中的其他类访问。
②修饰符(Modifiers):可以控制这些元素的访问级别、继承性、抽象性、静态性等,用于修饰类、字段、方法和内部类等元素的特性。
abstract
:指定该类是抽象的,不能被实例化,只能作为父类被继承,可以修饰类、方法
final
:指定该类是最终的,不能被继承,可以修饰类、方法、变量
static
:指定该类是静态的,可以在没有实例对象的情况下访问其中的静态成员,可以修饰类、方法、变量
③类名(Class Name):类的标识符,首字母通常大写,应该遵循驼峰命名规则
④继承(Inheritance):可选项,使用关键字 extends
指定父类。一个类最多只能继承一个父类,但可以实现多个接口
⑤实现的接口(Implemented Interfaces):可选项,使用关键字 implements
指定实现的接口。多个接口之间用逗号分隔
⑥类体(Class Body):使用一对大括号 {}
包裹起来,包含类的成员变量、构造函数、方法等
1.2、成员变量
[Access Modifier] [Modifiers] [Data Type] [Variable Name] [Initial Value]
具体实例:
public class MyClass {
private static final int MAX_COUNT = 100;
protected String name;
public int age = 18;
}
详解:
①访问修饰符(Access Modifier):用于控制成员变量的访问级别,同理类
②修饰符(Modifiers):用于修饰成员变量的特性,同理类
③数据类型(Data Type):指定成员变量的数据类型,可以是任何合法的Java类型,包括基本数据类型和引用类型等
④变量名(Variable Name):成员变量的标识符,应该遵循驼峰命名规则
⑤初始值(Initial Value):如果需要,在声明成员变量时可以直接指定初始值
1.3、成员方法
[Access Modifier] [Modifiers] [Return Type] [Method Name]([Parameter List]) throws [Exception List] [Method Body]
具体实例:
public static int add(int a, int b) throws Exception {
// 方法的实现
}
详解:
①访问修饰符(Access Modifier):用于控制方法的访问级别
②修饰符(Modifiers):用于修饰方法的特性
③返回类型(Return Type):指定方法返回值的类型,可以是任何合法的Java类型,如果方法没有返回值,则使用 void
关键字表示
④方法名(Method Name):方法的标识符,应该遵循驼峰命名规则
⑤参数列表(Parameter List):用于传递方法所需的参数,每个参数由类型和名称组成,多个参数之间用逗号分隔
⑥异常列表(Exception List):用于指定方法可能抛出的异常,多个异常之间用逗号分隔
⑦方法体(Method Body):使用一对大括号 {}
包裹起来,包含方法的实际实现
2、接口和抽象类的区别
2.1、抽象类:
**2.1.1、引出:**随着继承层次中一个个新子类的定义,类变得越来越具体,而父类则更一般,更通用。类的设计应该保证父类和子类能够共享特征。有时将一个父类设计得非常抽象,以至于它没有具体的实例,这样的类叫做抽象类。(即将父类抽象,将子类具体,我们创建对象,通过子类去创建) 一般,我们通过abstract关键词来修饰抽象类。
2.1.2、修饰对象: 类、方法
2.1.3、抽象类: 通过abstract修饰类
示例:
//抽象类
abstract class AA{
//……
}
特点:
①此类不能实例化
②抽象类中仍然可以提供构造器,初始化抽象类中的成员变量(虽然自己不能去实例化对象,但抽象类的子类是要进行实例化的,便于子类实例化时候调用父类构造器,涉及子类对象实例化的全过程)
补充:如果一个类没有显式地定义任何构造器,那么编译器会为该类提供一个默认的无参构造器。子类调用父类构造器的方式是使用 super()
关键字,括号中是对应的参数列表。这样就可以显式地调用父类的构造器。如果没有显式地调用父类构造器,那么编译器会自动在子类构造器的第一行插入 super()
,默认调用父类的无参构造器。需要注意的是,子类调用父类构造器的语句必须作为子类构造器的第一条语句。这是因为在创建子类对象之前,必须先调用父类构造器进行父类的初始化工作。
③抽象类存在的意义是为了被继承,抽象类都会有对应的子类,让子类对象实例化。抽象类可以包含具体方法的实现,这些方法可以被子类直接调用,子类也可以覆盖(重写)或扩展父类的具体方法。但是抽象类中的抽象方法没有具体的实现,必须要在子类中进行实现。抽象类的存在主要是为了提供一种抽象的模板,让子类根据自身的需要进行具体的实现。
2.1.4、抽象方法:
示例:
abstract class AA {
public abstract double MethodA();
}
特点:
①抽象方法只有方法的声明,没有方法体。
②包含抽象方法的类一定是一个抽象类(如果不是抽象类,那么类可以实例化,那么可以去调这个抽象方法,但抽象方法不应该被调用)。反之,抽象类中可以没有抽象方法的。
③若子类重写了父类中的所有的抽象方法后,此子类方可实例化;若子类没有重写父类中的所有抽象方法,则意味着次子类也是一个抽象类,需要使用abstract去修饰一下。
2.1.5 abstract
使用的注意点和扩展:
①abstract
不能用来修饰属性、构造器等结构。
②abstract
不能用来修饰私有方法(私有方法不能被重写,子类无法访问父类的私有方法)、静态方法(static修饰的方法不需要重写,不依赖于实例化对象,不存在被子类所覆盖的情况)、final的方法(不能被重写)、final的类(不能继承,那么该抽象类不能被子类创建实例)。
③创建抽象类的匿名子类对象
匿名对象:当对对象的方法只调用一次时,可以用匿名对象来完成,这样写比较简化。
匿名类:也叫匿名内部类,是内部类的一种匿名类在类中只能使用一次,它通常用来简化代码的编写,但使用匿名类还有一个前提条件:它必须继承一个父类或者一个接口,因此这个匿名类会被默认为这个父类或者接口的子类或实现。不用再去特意新建一个类,然后再通过该类去进行实例化。
package Anonymous_class;
public class Test {
public static void main(String[] args) {
//1、创建一个非匿名子类
//那么就要先创建一个该抽象类Person的子类,并重写Person中的抽象方法
//然后通过该子类去实例化,即是创建一个非匿名子类
//2、创建一个匿名子类的对象:p
//利用多态,并在new的同时为子类对象进行重写抽象父类的抽象方法
//优点:不用再去通过建立一个子类,然后将该子类进行实例化。
Person p = new Person(){
@Override
public void breath() {
// TODO Auto-generated method stub
System.out.println("呼吸!");
}
@Override
public void eat() {
// TODO Auto-generated method stub
System.out.println("吃东西!");
}
};
Test.show(p);
//或是省略当前类名,直接调用:show(p);
//3、创建匿名子类的匿名对象:
Test.show(new Person(){
@Override
public void breath() {
// TODO Auto-generated method stub
System.out.println("呼吸!");
}
@Override
public void eat() {
// TODO Auto-generated method stub
System.out.println("吃东西!");
}
});
}
public static void show(Person person){
person.eat();
person.breath();
}
}
abstract class Person{
private String name;
private int age;
public abstract void breath();
public abstract void eat();
}
2.2、接口类
2.2.1、引出:
- 有时必须从几个类中派生出一个子类,继承他们所有的属性和方法,但Java不支持多重继承。
- 有时必须从几个类中抽取一些共同的行为特征,而他们之间又没有继承的关系,仅仅是具有相同的行为特征而已。比如说手机、Mp3都是通过USB接口。
- 接口就是规范,定义的是一组规则,体现了显示世界中“如果你是/要……则必须能……”的思想。继承是一个是不是的关系,而接口实现则是能不能的关系。
- 接口的本质是契约,标准,规范,就像我们的法律一样,制定好以后大家都要遵循。
2.2.2、修饰对象: 类
2.2.3、接口的使用:
示例:接口用interface
修饰
//接口
interface BB {
//……
}
① 接口使用interface来定义(语法格式 interface 名称{})
② Java中,接口和类是并列的结构
③定义接口中的成员时,JDK7:以前,只能定义全局常量(public static final的,书写可以省略)和抽象方法(public abstract的,书写可以省略)。
JDK8:除了定义全局常量和抽象方法以外,还可以定义静态方法、默认方法(略)
public interface CompareA {
//静态方法
public static void method1(){
System.out.println("comparaA:北京");
}
//默认方法
public default void method2(){
System.out.println("comparaA:上海");
}
default void method3(){
System.out.println("comparaA:上海");
}
}
public interface CompareB {
default void method3(){
System.out.println("comparaB:上海");
}
}
public class SubClassTest {
public static void main(String[] args) {
SubClass s = new SubClass();
//知识点1:接口中定义的静态方法,只能通过接口来调用
CompareA.method1();
//知识点2:通过实现类的对象,可以调用接口中的默认方法
//如果实现类重写了接口中的默认方法,调用时,仍然调用的是重写的方法
s.method2();
//知识点3:如果实现类[对接口](或子类[对父类])继承的父类和实现的接口中声明了同名同参数的方法
//那么子类在没有重写此方法的情况下,默认调用的是父类中的同名同参数方法——类优先原则(接口稍后原则)
//对于属性,则没有先后,会出现编译错误
//知识点4:如果实现类实现了多个接口,而这多个接口中定义了同名同参数的默认方法method3
//那么在实现类没有重写此方法的情况下,会报错。-- 接口冲突
//这就需要我们必须在实现类中重写此方法!
s.method3();
s.mymethod();
}
}
class SubClass extends SuperClass implements CompareA,CompareB{
//重写接口默认方法
public void method2(){
System.out.println("SubClass:上海 ");
}
//知识点5:如何在子类(或实现类)的方法中调用父类、接口中被重写的方法
public void mymethod(){
this.method2(); //调用自己定义的重写方法
super.method3();//调用父类中声明的
//调用接口中的默认方法
CompareA.super.method3();
CompareB.super.method3();
}
}
public class SuperClass {
public void method3(){
System.out.println("superclass:北京");
}
}
④ 接口中不能定义构造器,即接口无法实例化,它只定义了一组方法的规范,而没有实现,子类实例化的过程与普通的类不同,子类需要通过实现接口并覆盖(实现)接口中的所有方法来完成实例化。
⑤ Java开发中,接口通过让类去实现的方式(implements)来使用。如果实现类覆盖了接口中的所有抽象方法,则次实现类就可以实例化;如果实现类没有覆盖接口中的所有的抽象方法,则此实现类仍为一个抽象类。
⑥Java中的类可以实现多个接口,弥补了Java单继承性的局限性
⑦对于一个类,先继承父类,后写需要实现的接口。格式:class A extends BB implements CC,DD,EE{};
⑧接口与接口之间可以继承,而且可以多继承。interface CC extends AA,BB{};
2.2.4、接口的特点:
①接口的具体使用能够体现多态性,通过其子类去进行实例化。
②接口,实际上可以看做是一种规范。对于项目的具体需求是多变的,我们必须以不变应万变才能从容开发,此处的不变就是规范,因此我们开发项目往往都是面向接口编程(接口的主要用途就是被实现类实现)
③开发中,体会面向接口编程。
2.2.5、接口使用的注意点和扩展
① 接口匿名实现类对象
package Anonymous_Interface;
public class USBTest {
public static void main(String[] args) {
Computer com = new Computer();
//1.创建了接口的非匿名实现类的非匿名对象flash
Flash flash = new Flash();
com.transferData(flash);
System.out.println("****************************");
//2.创建了接口的非匿名实现类的匿名对象
com.transferData(new Printer());
System.out.println("****************************");
//3.创建了接口的匿名实现类的非匿名对象
//匿名实现类即不适用其可以实例化的子类进行实例化
//直接通过子类进行实例化
USB phone = new USB(){
@Override
public void start() {
// TODO Auto-generated method stub
System.out.println("U盘开始工作!");
}
@Override
public void end() {
// TODO Auto-generated method stub
System.out.println("U盘结束工作!");
}
};
com.transferData(phone);
System.out.println("****************************");
//4.创建了接口的匿名实现类的匿名对象
com.transferData(new USB(){
@Override
public void start() {
// TODO Auto-generated method stub
System.out.println("U盘开始工作!");
}
@Override
public void end() {
// TODO Auto-generated method stub
System.out.println("U盘结束工作!");
}
});
}
}
class Computer{
public void transferData(USB usb){
usb.start();
System.out.println("具体传输数据的细节");
usb.end();
}
}
class Flash implements USB{
@Override
public void start() {
// TODO Auto-generated method stub
System.out.println("U盘开始工作!");
}
@Override
public void end() {
// TODO Auto-generated method stub
System.out.println("U盘结束工作!");
}
}
class Printer implements USB{
@Override
public void start() {
// TODO Auto-generated method stub
System.out.println("打印机开始工作!");
}
@Override
public void end() {
// TODO Auto-generated method stub
System.out.println("打印机结束工作!");
}
}
//定义接口
interface USB{
public abstract void start();
public abstract void end();
}
1.4 抽象类和接口类的相同点
①接口和抽象类都无法直接实例化
②实现接口或者继承抽象类的普通子类都必须实现其中的抽象方法
1.5 抽象类和接口类的不同点
①抽象类是一种特殊的类,它不能被实例化,只能被继承;接口是一种规范或约定,它只包含方法的声明和常量的定义,而没有任何方法的实现
②抽象类可以包含普通方法和代码块,接口中只能包含抽象方法、静态方法和默认方法
③抽象类可以有构造方法,而接口没有
④抽象类中的成员变量可以是各种类型的,接口的成员变量只能是 public static final 类型的,并且必须赋值
⑤使用extends继承抽象类,使用implements关键字实现接口
⑥一个抽象类可以实现若干个接口,接口不能继承抽象类,接口可以使用extends继承多个父类接口
⑦一个子类只能继承一个抽象类,一个子类可以实现多个接口
2、重载和重写的区别
重载:
①对象:重载的对象是同一个类中的方法
②概念:在同一类中,允许存在一个以上的同名方法,只要他们的参数个数或者参数类型 不同即可。
③构成重载的特点:与方法的权限修饰符、返回值类型、形参名无关,只看参数列表,且参数列表必须不同。调用的时候,根据方法的参数列表的不同来进行区分。可以概括为:“两同一不同”:同一个类、相同方法名,参数列表不同(参数个数不同,参数类型不同,参数排列顺序不同)。
④确定重载方法:先看方法名,再看参数列表
重写:
①对象:重写的对象是子类中的方法。
②概念:在子类中可以根据需要对从父类中继承来的同名同参数的方法进行改造,也称为方法的重置、覆盖。在程序执行时,子类的方法将覆盖父类的方法。重写以后,当创建子类对象以后,通过子类对象调用父类中的同名同参数的方法时,实际执行的是子类重写的父类的方法。
③重写的规定:
方法的声明: 权限修饰符 返回值类型 方法名(形参列表)throws 异常类型{方法体……}
约定:子类中的叫重写的方法,父类中的叫被重写的方法。
(1)子类重写的方法的方法名和形参列表与父类被重写的方法的方法名和形参列表相同。(方法名和形参列表唯一确定某一个方法)
(2)子类重写的方法的权限修饰符不小于父类被重写的方法的权限修饰符。(特殊情况:子类不能重写父类中声明为private权限的方法,只能说在子类中新写了一个方法,因为其看不到父类中的private方法)
(3)返回值类型:
父类被重写的方法的返回值是void,则子类重写的方法的返回值类型只能是void。
父类被重写的方法的返回值类型是A类型,则子类重写的方法的返回值类型可以是A类或A类的子类。
父类被重写的方法的返回值类型是基本数据类型,则子类重写的方法的返回值类型必须是相同的基本数据类型。
(4)子类重写的方法抛出的异常类型不大于父类被重写的异常类型。
3、==和equals的区别
==:
①对于基本类型比较的是其值
②对于引用类型比较的是其内存地址
equals:
equals是Object的方法,因此对于所有的类都有equals方法(所有类都是Object的子类)
本质上与==一样,但是有些类重写了equals方法,比如String的equals被重写后,比较的是字符值,另外重写了equlas后,也必须重写hashcode()方法,因为在Java中,equals和hashCode方法是配对使用的。
equals方法用于判断两个对象是否相等,而hashCode方法则用于计算对象的哈希码值。哈希码值在集合类(如HashMap、HashSet等)中广泛使用,用于确定对象在集合中的存储位置。根据Java规范,如果两个对象使用equals方法判断为相等,那么它们的hashCode值也必须相等。反之,如果两个对象的hashCode值相等,不一定代表它们相等,只是有可能相等。
补充: