继承
继承是一种面向对象编程(OOP)特性,它允许一个类(称为子类或派生类)继承另一个类(称为父类或基类)的属性(如方法和字段)。通过继承,子类可以复用父类的代码,并且可以扩展或修改父类的行为以实现新的功能。
- 继承父类的属性和方法
// 公共类
class Animal {
String name;
public void eat() {
System.out.println(this.name + "正在吃");
}
}
// 狗
class Dog extends Animal {
public Dog(String name) {
this.name = name;
}
}
// 猫
class Cat extends Animal {
public Cat(String name) {
this.name = name;
}
}
继承的特性
- 子类拥有父类非 private 的属性、方法。
- 子类可以拥有自己的属性和方法,即子类可以对父类进行扩展。
- 子类可以用自己的方式实现父类的方法。
- 继承是单继承,但是可以多重继承。单继承就是一个子类只能继承一个父类,多重继承就是,例如 B 类继承 A 类,C 类继承 B 类,所以按照关系就是 B 类是 C 类的父类,A 类是 B 类的父类
super 与 this 关键字
super
关键字:我们可以通过 super
关键字来实现对父类成员的访问,用来引用当前对象的父类。
this
关键字:指向自己的引用,引用当前对象,即它所在的方法或构造函数所属的对象实例。
class Animal {
void eat() {
System.out.println("animal : eat");
}
}
class Dog extends Animal {
void eat() {
System.out.println("dog : eat");
}
void eatTest() {
this.eat(); // this 调用自己的方法
super.eat(); // super 调用父类方法
}
}
public class Test {
public static void main(String[] args) {
Animal a = new Animal();
a.eat();
Dog d = new Dog();
d.eatTest();
}
}
final 关键字
final
可以用来修饰变量(包括类属性、对象属性、局部变量和形参)、方法(包括类方法和对象方法)和类。
final
含义为 “最终的”。
使用 final
关键字声明类,就是把类定义定义为最终类,不能被继承,或者用于修饰方法,该方法不能被子类重写
构造器
子类是不继承父类的构造器(构造方法或者构造函数)的,它只是调用(隐式或显式)。如果父类的构造器带有参数,则必须在子类的构造器中显式地通过 super
关键字调用父类的构造器并配以适当的参数列表。
如果父类构造器没有参数,则在子类的构造器中不需要使用 super
关键字调用父类构造器,系统会自动调用父类的无参构造器。
class SuperClass {
private int n;
// 无参数构造器
public SuperClass() {
System.out.println("SuperClass()");
}
// 带参数构造器
public SuperClass(int n) {
System.out.println("SuperClass(int n)");
this.n = n;
}
}
// SubClass 类继承
class SubClass extends SuperClass {
private int n;
// 无参数构造器,自动调用父类的无参数构造器
public SubClass() {
System.out.println("SubClass()");
}
// 带参数构造器,调用父类中带有参数的构造器
public SubClass(int n) {
super(300);
System.out.println("SubClass(int n): " + n);
this.n = n;
}
}
重写和重载
重写
重写(Override)是指子类定义了一个与其父类中具有相同名称、参数列表和返回类型的方法,并且子类方法的实现覆盖了父类方法的实现。
// 公共类
class Animal {
String name;
public void eat() {
System.out.println(this.name + "正在吃");
}
}
// 狗
class Dog extends Animal {
public Dog(String name) {
this.name = name;
}
@Override
public void eat() {
// 调用父类的eat方法
super.eat();
System.out.println(this.name + "正在吃骨头");
}
}
重写规则
-
参数列表与被重写方法的参数列表必须完全相同。
-
返回类型与被重写方法的返回类型可以不相同,但是必须是父类返回值的派生类(java5 及更早版本返回类型要一样,java7 及更高版本可以不同)。
-
访问权限不能比父类中被重写的方法的访问权限更低。例如:如果父类的一个方法被声明为 public,那么在子类中重写该方法就不能声明为 protected。
-
父类的成员方法只能被它的子类重写。
-
声明为 final 的方法不能被重写。
-
声明为 static 的方法不能被重写,但是能够被再次声明。
-
子类和父类在同一个包中,那么子类可以重写父类所有方法,除了声明为 private 和 final 的方法。
-
子类和父类不在同一个包中,那么子类只能够重写父类的声明为 public 和 protected 的非 final 方法。
-
构造方法不能被重写。
重载
重载(overloading) 是在一个类里面,方法名字相同,而参数不同。返回类型可以相同也可以不同。
每个重载的方法(或者构造函数)都必须有一个独一无二的参数类型列表。
最常用的地方就是构造器的重载。
构造器重载
class Animal {
String name;
public Animal() {
}
public Animal(String name) {
this.name = name;
}
}
方法重载
class Dog extends Animal {
public Dog(String name) {
super(name);
}
public void eat() {
System.out.println("狗吃骨头");
}
public void eat(String name) {
System.out.println(name + "吃骨头");
}
}
Dog dog = new Dog("旺财");
dog.eat();
dog.eat("旺财");
多态
多态是同一个行为具有多个不同表现形式或形态的能力。
多态存在的三个必要条件
- 继承
- 重写
- 父类引用指向子类对象:
Parent p = new Child();
class Animal {
String name;
public Animal(String name) {
this.name = name;
}
public void eat() {
System.out.println(this.name + "吃东西");
}
}
// 狗
class Dog extends Animal {
public Dog(String name) {
super(name);
}
@Override
public void eat() {
System.out.println(this.name + "吃骨头");
}
}
class Cat extends Animal {
public Cat(String name) {
super(name);
}
@Override
public void eat() {
System.out.println(this.name + "吃鱼");
}
}
ArrayList<Animal> animals = new ArrayList<>();
animals.add(new Dog("旺财"));
animals.add(new Cat("小花"));
animals.forEach(animal -> animal.eat());
抽象类
在面向对象的概念中,所有的对象都是通过类来描绘的,但是反过来,并不是所有的类都是用来描绘对象的,如果一个类中没有包含足够的信息来描绘一个具体的对象,这样的类就是抽象类。
抽象类除了不能实例化对象之外,类的其它功能依然存在,成员变量、成员方法和构造方法的访问方式和普通类一样。
由于抽象类不能实例化对象,所以抽象类必须被继承,才能被使用。
abstract class Animal {
String name;
public Animal(String name) {
this.name = name;
}
public void eat() {
System.out.println(this.name + "吃东西");
}
}
抽象方法
abstract class Animal {
String name;
public Animal(String name) {
this.name = name;
}
public void eat() {
System.out.println(this.name + "吃东西");
}
// 使用abstract关键字,声明一个抽象方法
abstract void move();
}
// 狗
class Dog extends Animal {
public Dog(String name) {
super(name);
}
@Override
public void eat() {
System.out.println(this.name + "吃骨头");
}
// 子类需要实现抽象方法
@Override
void move() {
}
}
- 抽象类中不一定包含抽象方法,但是有抽象方法的类必定是抽象类。
- 任何子类必须重写父类的抽象方法,或者声明自身为抽象类。
- 继承抽象方法的子类必须重写该方法。否则,该子类也必须声明为抽象类。最终,必须有子类实现该抽象方法,否则,从最初的父类到最终的子类都不能用来实例化对象。
- 抽象类不能被实例化
- 抽象类中的抽象方法只是声明,不包含方法体,就是不给出方法的具体实现也就是方法的具体功能。
- 构造方法,类方法(用 static 修饰的方法)不能声明为抽象方法。
封装
在面向对象程式设计方法中,封装(英语:Encapsulation)是指一种将抽象性函式接口的实现细节部分包装、隐藏起来的方法。可以防止外部代码意外地或恶意地修改对象的状态。
public class Person {
private int age; // 私有变量
public void setAge(int age) {
if (age > 0 && age < 150) { // 验证年龄的有效性
this.age = age;
} else {
System.out.println("年龄无效!");
}
}
public int getAge() {
return this.age;
}
}
age
变量是私有的,不能从类的外部直接访问。setAge
方法允许外部代码更改年龄,但在设置之前会检查提供的值是否合理。getAge
方法允许外部代码获取年龄的值。
接口
接口是抽象方法的集合,接口通常以interface
来声明。一个类通过继承接口的方式,从而来继承接口的抽象方法。
接口并不是类,编写接口的方式和类很相似,但是它们属于不同的概念。类描述对象的属性和方法。接口则包含类要实现的方法。
除非实现接口的类是抽象类,否则该类要定义接口中的所有方法。
接口无法被实例化,但是可以被实现。一个实现接口的类,必须实现接口内所描述的所有方法,否则就必须声明为抽象类。
// 接口
interface Animal {
void eat();
void move();
}
// 狗
class Dog implements Animal {
private String name;
public Dog(String name) {
this.name = name;
}
@Override
public void eat() {
System.out.println(this.name + "爱吃肉");
}
@Override
public void move() {
System.out.println(this.name + "爱跑步");
}
}
反射
反射是一种在运行时分析类、接口、字段和方法的能力。反射允许程序在执行期间查询和“反照”自身,并且可以使用这种类型的信息来直接调用任意确定的方法或访问字段。
反射在Java中的主要用途包括:
- 动态加载类:可以在运行时根据字符串动态加载类,并实例化对象。
- 访问私有成员:反射可以突破访问控制,即使成员是私有的也可以访问。
- 调用方法:可以动态地调用方法,即使是在编译时不知道具体方法名的情况。
- 处理数组:反射可以用来创建和操作数组。
- 操作注解:反射可以用来获取类上的注解信息,并进行相应的处理
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
public class ReflectionExample {
public static void main(String[] args) throws Exception {
// 获取 Class 对象
Class<?> clazz = Person.class;
// 创建对象
Constructor<?> constructor = clazz.getConstructor(String.class, int.class);
Object person = constructor.newInstance("John", 30);
// 访问字段
Field nameField = clazz.getDeclaredField("name");
nameField.setAccessible(true);
System.out.println("Name: " + nameField.get(person));
// 修改字段
nameField.set(person, "Doe");
System.out.println("Updated Name: " + nameField.get(person));
// 调用方法
Method greetMethod = clazz.getMethod("greet", String.class);
greetMethod.invoke(person, "World");
}
}
class Person {
private String name;
private int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
public void greet(String message) {
System.out.println(name + " says: " + message);
}
}