目录
构造方法有哪些特点?是否可被 override?
面向对象三大特征
封装
继承
多态
接口和抽象类有什么共同点和区别?
接口和抽象类的共同点
接口和抽象类的区别
深拷贝和浅拷贝区别了解吗?什么是引用拷贝?
浅拷贝
深拷贝
构造方法有哪些特点?是否可被 override?
- 名称与类名相同:构造方法的名称必须与类名完全一致。
- 没有返回值:构造方法没有返回类型,且不能使用
void
声明。 - 自动执行:在生成类的对象时,构造方法会自动执行,无需显式调用。
构造方法不能被重写(override),但可以被重载(overload)。因此,一个类中可以有多个构造方法,这些构造方法可以具有不同的参数列表,以提供不同的对象初始化方式。
面向对象三大特征
封装
封装是指把一个对象的状态信息(也就是属性)隐藏在对象内部,不允许外部对象直接访问对象的内部信息。但是可以提供一些可以被外界访问的方法来操作属性。就好像我们看不到挂在墙上的空调的内部的零件信息(也就是属性),但是可以通过遥控器(方法)来控制空调。如果属性不想被外界访问,我们大可不必提供方法给外界访问。但是如果一个类没有提供给外界访问的方法,那么这个类也没有什么意义了。就好像如果没有空调遥控器,那么我们就无法操控空凋制冷,空调本身就没有意义了(当然现在还有很多其他方法 ,这里只是为了举例子)。
public class Test4 {private int age; //私有化ageprivate String name; // 私有化name// 获取age的方法public int getAge() {return age;}// 设置age的方法 public void setAge(int age) {this.age = age;}// 获取name的方法public String getName() {return name;}// 设置name的方法public void setName(String name) {this.name = name;}
}
继承
不同类型的对象,相互之间经常有一定数量的共同点。例如,小明同学、小红同学、小李同学,都共享学生的特性(班级、学号等)。同时,每一个对象还定义了额外的特性使得他们与众不同。例如小明的数学比较好,小红的性格惹人喜爱;小李的力气比较大。继承是使用已存在的类的定义作为基础建立新类的技术,新类的定义可以增加新的数据或新的功能,也可以用父类的功能,但不能选择性地继承父类。通过使用继承,可以快速地创建新的类,可以提高代码的重用,程序的可维护性,节省大量创建新类的时间 ,提高我们的开发效率。
关于继承如下 3 点请记住:
- 子类拥有父类对象所有的属性和方法(包括私有属性和私有方法),但是父类中的私有属性和方法子类是无法访问,只是拥有。
- 子类可以拥有自己属性和方法,即子类可以对父类进行扩展。
- 子类可以用自己的方式实现父类的方法。
// 学生类(父类)
class Student {private String className;private String studentId;// 构造函数public Student(String className, String studentId) {this.className = className;this.studentId = studentId;}// 获取班级public String getClassName() {return className;}// 设置班级public void setClassName(String className) {this.className = className;}// 获取学号public String getStudentId() {return studentId;}// 设置学号public void setStudentId(String studentId) {this.studentId = studentId;}// 打印学生信息public void printStudentInfo() {System.out.println("班级: " + className + ", 学号: " + studentId);}
}// 小明同学:擅长数学
class XiaoMing extends Student {private boolean goodAtMath;// 构造函数public XiaoMing(String className, String studentId, boolean goodAtMath) {super(className, studentId);this.goodAtMath = goodAtMath;}// 获取是否擅长数学public boolean isGoodAtMath() {return goodAtMath;}// 打印小明信息@Overridepublic void printStudentInfo() {super.printStudentInfo();System.out.println("小明擅长数学: " + goodAtMath);}
}// 小红同学:性格惹人喜爱
class XiaoHong extends Student {private String personality;// 构造函数public XiaoHong(String className, String studentId, String personality) {super(className, studentId);this.personality = personality;}// 获取性格public String getPersonality() {return personality;}// 打印小红信息@Overridepublic void printStudentInfo() {super.printStudentInfo();System.out.println("小红的性格: " + personality);}
}// 小李同学:力气比较大
class XiaoLi extends Student {private boolean strong;// 构造函数public XiaoLi(String className, String studentId, boolean strong) {super(className, studentId);this.strong = strong;}// 获取是否力气大public boolean isStrong() {return strong;}// 打印小李信息@Overridepublic void printStudentInfo() {super.printStudentInfo();System.out.println("小李力气大: " + strong);}
}// 测试类
class Main {public static void main(String[] args) {XiaoMing xiaoMing = new XiaoMing("三年级一班", "001", true);XiaoHong xiaoHong = new XiaoHong("三年级一班", "002", "惹人喜爱");XiaoLi xiaoLi = new XiaoLi("三年级一班", "003", true);xiaoMing.printStudentInfo();xiaoHong.printStudentInfo();xiaoLi.printStudentInfo();}
}
多态
多态,顾名思义,表示一个对象具有多种的状态,具体表现为父类的引用指向子类的实例。
多态的特点:
- 对象类型和引用类型之间具有继承(类)/实现(接口)的关系;
- 引用类型变量发出的方法调用的到底是哪个类中的方法,必须在程序运行期间才能确定;
- 多态不能调用“只在子类存在但在父类不存在”的方法;
- 如果子类重写了父类的方法,真正执行的是子类重写的方法,如果子类没有重写父类的方法,执行的是父类的方法。
接口和抽象类有什么共同点和区别?
接口和抽象类的共同点
- 实例化:接口和抽象类都不能直接实例化,只能被实现(接口)或继承(抽象类)后才能创建具体的对象。
- 抽象方法:接口和抽象类都可以包含抽象方法。抽象方法没有方法体,必须在子类或实现类中实现。
接口和抽象类的区别
- 设计目的:接口主要用于对类的行为进行约束,你实现了某个接口就具有了对应的行为。抽象类主要用于代码复用,强调的是所属关系。
- 继承和实现:一个类只能继承一个类(包括抽象类),因为 Java 不支持多继承。但一个类可以实现多个接口,一个接口也可以继承多个其他接口。
- 成员变量:接口中的成员变量只能是public static final类型的,不能被修改且必须有初始值。抽象类的成员变量可以有任何修饰符(private, protected, public),可以在子类中被重新定义或赋值。
- 方法:
- Java 8 之前,接口中的方法默认是public abstract ,也就是只能有方法声明。自 Java 8 起,可以在接口中定义 default(默认) 方法和static (静态)方法。 自 Java 9 起,接口可以包含private方法。
- 抽象类可以包含抽象方法和非抽象方法。抽象方法没有方法体,必须在子类中实现。非抽象方法有具体实现,可以直接在抽象类中使用或在子类中重写。
在 Java 8 及以上版本中,接口引入了新的方法类型:default 方法、static 方法和 private 方法。这些方法让接口的使用更加灵活。
Java 8 引入的default 方法用于提供接口方法的默认实现,可以在实现类中被覆盖。这样就可以在不修改实现类的情况下向现有接口添加新功能,从而增强接口的扩展性和向后兼容性。
public interface Test5 {default void defaultMethod() {System.out.println("This is a default method.");}
}
Java 8 引入的static 方法无法在实现类中被覆盖,只能通过接口名直接调用( MyInterface.staticMethod()),类似于类中的静态方法。static 方法通常用于定义一些通用的、与接口相关的工具方法,一般很少用。
public interface Test5 {static void defaultMethod() {System.out.println("This is a default method.");}// default void defaultMethod() {
// System.out.println("This is a default method.");
// }
}
Java 9 允许在接口中使用 private 方法。private方法可以用于在接口内部共享代码,不对外暴露。
public interface Test6 {// static 方法static void staticMethod() {commonMethod();}// 私有静态方法,可以被 static 和 default 方法调用private static void commonMethod() {System.out.println("This is a private method used internally.");}// default 方法default void defaultMethod() {commonMethod();}// 实例私有方法,只能被 default 方法调用。private void instanceCommonMethod() {System.out.println("This is a private instance method used internally.");}
}
深拷贝和浅拷贝区别了解吗?什么是引用拷贝?
关于深拷贝和浅拷贝区别,我这里先给结论:
- 浅拷贝:浅拷贝会在堆上创建一个新的对象(区别于引用拷贝的一点),不过,如果原对象内部的属性是引用类型的话,浅拷贝会直接复制内部对象的引用地址,也就是说拷贝对象和原对象共用同一个内部对象。
- 深拷贝:深拷贝会完全复制整个对象,包括这个对象所包含的内部对象。
上面的结论没有完全理解的话也没关系,我们来看一个具体的案例!
浅拷贝
浅拷贝的示例代码如下,我们这里实现了 Cloneable 接口,并重写了 clone() 方法。 clone() 方法的实现很简单,直接调用的是父类 Object 的 clone() 方法。
public class Address implements Cloneable{private String name;// 省略构造函数、Getter&Setter方法@Overridepublic Address clone() {try {return (Address) super.clone();} catch (CloneNotSupportedException e) {throw new AssertionError();}}
}public class Person implements Cloneable {private Address address;// 省略构造函数、Getter&Setter方法@Overridepublic Person clone() {try {Person person = (Person) super.clone();return person;} catch (CloneNotSupportedException e) {throw new AssertionError();}}
}
测试
Person person1 = new Person(new Address("武汉"));
Person person1Copy = person1.clone();
// true
System.out.println(person1.getAddress() == person1Copy.getAddress());
从输出结构就可以看出, person1 的克隆对象和 person1 使用的仍然是同一个 Address 对象。
深拷贝
这里我们简单对 Person 类的 clone() 方法进行修改,连带着要把 Person 对象内部的 Address 对象一起复制。
@Override
public Person clone() {try {Person person = (Person) super.clone();person.setAddress(person.getAddress().clone());return person;} catch (CloneNotSupportedException e) {throw new AssertionError();}
}
测试
Person person1 = new Person(new Address("武汉"));
Person person1Copy = person1.clone();
// false
System.out.println(person1.getAddress() == person1Copy.getAddress());
从输出结构就可以看出,显然 person1 的克隆对象和 person1 包含的 Address 对象已经是不同的了。 那什么是引用拷贝呢? 简单来说,引用拷贝就是两个不同的引用指向同一个对象。