java面向对象程序设计—下
封装
封装是将一个类的实现细节隐藏起来,只暴露必要的接口给外部使用的过程。
类的封装
class student {
int age;
String name;
double score;
void introduce(){ //方法 = 函数
//不需要传参,因为Java面向对象,可以直接访问它里面的成员变量
System.out.println("name=" +name + "age="+age + "score="+score);
}
void testFunc(){
System.out.println("testFunc");
}
}
public class Test {
public static void main(String[] args) {
student stu1 = new student(); //实例化一个对象,类=模板,只有属性和方法,类不能直接使用需要先实例化一个对象
//struct student *p;
//p = malloc(sizeof(struct student));
stu1.age = 18;
stu1.name = "zheng";
stu1.score = 100;
stu1.introduce();//不需要传参,可以访问到同组变量
}
}
修饰符
- private(私有):
- 仅允许在声明了该成员的类中访问。
- 其他类无法直接访问私有成员,即使是同一个包中的类也不行。
- protected(受保护):
- 允许在声明了该成员的类、同一包中的类、以及该类的子类中访问。
- 类外部的非子类不能直接访问 protected 成员。
- public(公有):
- 可以被任意类访问,包括不同包中的类。
- 公有成员对所有类可见,无论是否在同一包中,也无论是否是子类。
- 默认(包访问,没有显式修饰符):
- 允许在声明了该成员的类和同一包中的类中访问。
- 对于同一个包中的其他类而言,没有声明访问修饰符的成员具有包访问权限。
访问修饰符 | 类内部 | 同一包中的类 | 子类 | 不同包中的类 |
---|---|---|---|---|
private | ✔ | ✘ | ✘ | ✘ |
protected | ✔ | ✔ | ✔ | ✘ |
public | ✔ | ✔ | ✔ | ✔ |
默认 | ✔ | ✔ | ✘ | ✘ |
构造方法
定义
构造方法是Java类中的一种特殊类型的方法,其主要用途是在对象创建时对对象进行初始化。构造方法的名称必须与类名相同,并且没有返回类型,包括 void
。当使用 new
关键字创建对象时,构造方法会被隐式调用,以确保对象在创建时处于一个合适的状态。
构造方法的特点
- 方法名与类名相同: 构造方法的名称必须与类名一致。
- 没有返回类型: 构造方法没有返回类型,甚至不需要声明
void
。 - 可以有参数: 构造方法可以接受参数,用于在对象创建时传递初始值。
- 可以有多个构造方法: 一个类可以有多个构造方法,称为构造方法的重载。这样可以提供不同的初始化方式。
构造方法的使用
public class Car {
private String make;
private String model;
private int year;
// 无参构造方法
public Car() {
make = "Unknown";
model = "Unknown";
year = 0;
}
// 有参构造方法
public Car(String make, String model, int year) {
this.make = make;
this.model = model;
this.year = year;
}
// 其他方法
public void displayInfo() {
System.out.println("Car: " + year + " " + make + " " + model);
}
public static void main(String[] args) {
// 使用无参构造方法创建对象
Car car1 = new Car();
car1.displayInfo(); // 输出:Car: 0 Unknown Unknown
// 使用有参构造方法创建对象
Car car2 = new Car("Toyota", "Camry", 2022);
car2.displayInfo(); // 输出:Car: 2022 Toyota Camry
}
}
构造方法的重载
构造方法的重载是指在同一个类中定义多个构造方法,这些构造方法具有相同的名称但参数列表不同的特征。通过构造方法的重载,可以为对象的创建提供不同的初始化方式。
// 无参构造方法
public Car() {
this("Unknown", "Unknown", 0); //无参构造方法的调用使用了 this() 关键字来调用有参构造方法,确保代码的复用。
}
// 有参构造方法
public Car(String make, String model, int year) {
this.make = make;
this.model = model;
this.year = year;
}
继承
定义
在Java中,继承是面向对象编程的一项重要特性,它允许一个类(子类)基于另一个类(父类)来定义,从而实现代码的重用和扩展。继承通过使用 extends
关键字来实现。
定义继承关系: 使用 extends
关键字,子类继承父类。
访问父类成员: 子类可以访问父类的非私有成员(字段和方法),即使它们在不同的包中。
方法重写: 子类可以重写父类的方法,提供自己的实现。使用 @Override
注解可以帮助检查是否正确地重写了父类的方法。
super关键字: super 关键字用于调用父类的方法或访问父类的字段。在构造方法中,super() 可以用于调用父类的构造方法,确保父类的初始化工作得以执行。
构造方法调用: 子类的构造方法可以通过 super()
调用父类的构造方法,确保父类的初始化工作得以执行。
多态性: 子类对象可以被视为父类对象,这允许在父类类型的引用中持有子类对象,并且在运行时调用适当的方法。
**构造方法的调用顺序:**在创建子类对象时,构造方法的调用顺序是先调用父类的构造方法,然后再调用子类的构造方法。这确保了对象的初始化按照正确的顺序进行。
继承的使用
代码复用
通过继承,可以在不重新编写代码的情况下使用现有类的功能,从而提高代码的复用性。子类继承父类的属性和方法,可以在子类中专注于扩展或修改特定行为。
// 父类
public class Animal {
public void eat() {
System.out.println("动物正在吃东西");
}
}
// 子类
public class Dog extends Animal {
public void bark() {
System.out.println("狗在叫");
}
}
// 在其他类中使用
public class Main {
public static void main(String[] args) {
Dog myDog = new Dog();
myDog.eat(); // 继承父类的方法
myDog.bark(); // 子类的方法
}
}
重写@Override
通过继承,可以创建一个基类,然后通过创建不同的子类来扩展或修改基类的功能。这种方式使得在应对变化时更加灵活,可以轻松地增加或修改类的行为。
// 父类
public class Shape {
public void draw() {
System.out.println("绘制形状");
}
}
// 子类扩展
public class Circle extends Shape {
@Override
public void draw() {
System.out.println("画一个圆");
}
}
// 子类修改
public class Square extends Shape {
@Override
public void draw() {
System.out.println("画一个正方形");
}
}
子类访问父类的成员
成员变量和成员方法的访问是基于就近原则的,这意味着如果子类中存在与父类相同名称的成员变量或成员方法,子类将优先使用自己的成员而不是父类的。
子类调用父类的构造方法
子类的构造方法可以使用 super
关键字调用父类的构造方法。这通常用于确保在创建子类对象时,首先执行父类的初始化工作。
当子类的构造方法中没有手动添加 super
关键字时,Java 编译器会自动调用父类的无参构造方法(如果存在的话)。这是因为每个类的构造方法的第一行,如果没有显式调用父类的构造方法(使用 super
关键字),则会默认调用父类的无参构造方法。
// 父类
public class Animal {
public Animal() {
System.out.println("父类无参构造");
}
}
// 子类
public class Dog extends Animal {
// 子类构造方法,没有手动添加super关键字
public Dog() {
System.out.println("子类无参构造");
}
public static void main(String[] args) {
// 创建子类对象
Dog myDog = new Dog();
}
}
子类访问自己的构造方法
this():
多态
定义
简单的说就是一个对象有多种形态,具体点说多态就是父类中的属性和方法被子类继承后,可以具有不同的数据类型或不同的行为,使父类中的同一个属性和方法在父类与各个子类具有不同的含义。
条件
继承:必须存在继承;
重写:子类必须对父类中的方法进行重写;
向上转型:通过父类的引用调用子类的重写的方法;
使用
class Animal{
public String name;
public int age;
public void eat(){
System.out.println(this.name + "吃饭");
}
public Animal(String name, int age) {
this.name = name;
this.age = age;
}
}
class Dog extends Animal{ //继承
@Override //重写
public void eat() { //向上转型
System.out.println(this.name + "吃狗粮");
}
public void sleep(){
System.out.println(this.name + "在睡觉");
}
public Dog(String name, int age) {
super(name, age);
}
}
class Cat extends Animal{ //继承
@Override //重写
public void eat() { //向上转型
System.out.println(this.name + "吃猫粮");
}
public void sleep(){
System.out.println(this.name + "在睡觉");
}
public Cat(String name, int age) {
super(name, age);
}
}
public class Test1 {
public static void eat(Animal animal){
animal.eat();
}
public static void main(String[] args) {
Cat cat = new Cat("花花",1);
Dog dog = new Dog("多多",2);
eat(cat);
eat(dog);
}
}
向上转型向下转型
向上转型
向上转型是将子类的对象引用赋给父类类型的引用,这样的引用可以操作子类对象,但只能调用父类中定义的方法。这是安全的,因为子类对象肯定包含了父类对象的所有特性。
Animal animal = new Dog(); // 向上转型
向下转型
向下转型是将父类类型的引用重新转换为子类类型的引用,以便调用子类中特有的方法。在进行向下转型之前,需要确保原始对象是可以被转型为目标类型的。
Dog dog = (Dog) animal; // 向下转型
优缺点
优点
可扩展能力更强
缺点
属性没有多态性,当父类和子类都有同名属性的时候,通过父类引用,只能引用父类自己的成员属性;
构造方法没有多态性;
final关键字
特点
修饰方法 不能重写父类的 final 方法
修饰变量 只能被赋值一次,而且一旦赋值后,就不能再修改
修饰类 阻止其他类继承该类,确保该类的实现不会被修改或者不希望有子类继承
抽象类
定义
抽象类是一种特殊的类,它不能被实例化,但可以被继承。抽象类通常用于提供一些共有的属性和方法,并定义一些抽象方法,这些抽象方法在具体的子类中必须被实现。
基本概念
定义抽象类 使用 abstract
关键字定义抽象类。抽象类可以包含抽象方法和具体方法。
继承抽象类 子类继承抽象类,并实现抽象类中的抽象方法。如果子类是抽象类,它可以选择性地实现抽象方法。
实例化抽象类 抽象类不能被实例化 可以通过向上转型的方式使用抽象类的引用指向其子类的对象
Animal myDog = new Dog(); // 向上转型
myDog.makeSound(); // 调用被子类实现的抽象方法
myDog.eat(); // 调用抽象类中的具体方法
抽象类中的构造方法 构造方法的主要作用是在创建子类对象时初始化抽象类的成员。
优点
强制子类实现抽象方法,确保了代码的一致性
提高代码重用,抽象类可以包含通用的属性和方法,这些代码可以被多个子类继承和共享
Java内部类
成员内部类
定义在一个类的内部,并且在类的成员位置。它可以访问外部类的所有成员,包括私有成员。
局部内部类
定义在一个方法或作用域内,只能在该方法或作用域内使用。
匿名内部类
没有显式地定义类的名称,通常用于实现接口或继承父类。
静态内部类
定义在类的内部,使用 static
关键字修饰,与外部类的实例无关,可以通过外部类直接访问。
异常
在Java中,异常是程序在执行期间发生的问题。Java异常处理机制用于捕获和处理这些问题,以确保程序不会因错误而崩溃。主要通过 try-catch
块来处理异常。
概念
顶级父类 Throwable
,它有两个主要的子类:Exception
和 Error
。
try {
// 可能抛出异常的代码
int result = divide(10, 0);
System.out.println("Result: " + result);
} catch (ArithmeticException e) {
// 捕获并处理异常
System.out.println("Error: " + e.getMessage());
} finally {
// 无论是否发生异常,都会执行的代码块
System.out.println("finally都会执行");
}
异常顺序
子类在前 父类在后
补充
finally
用于定义在无论是否发生异常都会执行的代码块。这种结构用于确保资源的释放或清理工作,无论是否发生异常,finally
中的代码都会被执行。