Bootstrap

Java三大特性:封装、继承、多态【详解】

封装

定义

隐藏对象的属性和实现细节,仅对外公开接口,控制在程序中属性的读取和修改的访问级别便是封装。

在开发中造一个类就是封装,有时也会说封装一个类。封装可以隐藏一些细节或者包含数据不能被随意修改。

比如这是一个敏感的数据,我不能够让别人直接操纵到这个数据,因此我用private把它包装起来,对外通过一个public的接口给别人使用它的权限。

在程序中,为防止类的属性被无操作导致程序错误,所以属性也是不允许外部直接操作的,要通过固定的放回操作,例如get/set方法

权限

提到封装,就不得不提到访问权限,它的目的是为了在代码中表示具体的属性或方法的访问权限

权限修饰符一共有4个:public、protected、缺省、private

访问范围privatefriendly(默认)protectedpublic
同一个类可访问可访问可访问可访问
同一个包中的其他类不可访问可访问可访问可访问
不同包中的子类不可访问不可访问可访问可访问
不同包中的非子类不可访问不可访问不可访问可访问

get/set方法

通过 public 的方法提供对私有字段的访问。这些方法通常称为 “getter” 和 “setter”。

具体写法:

  • boolean数据类型的写法是,get方法:isXxx()(xxx是属性的名字),set方法:setXxx(boolean xxx)。注意大写小,属性的名字是xxx,is和set后都首字母大写了;
  • 除了boolean数据类型,写法都是,get方法getXxx(),set方法setXxx(数据类型 参数)。

IDEA生成get/set方法

在这里插入图片描述

继承

定义

现实生活中,子女能够继承父母拥有的财产,而在程序中也是一样的。

程序中的继承指的是子类拥有父类的特征和行为,这是类与类之间的一种关系。它不仅可以继承父类的代码,还能对其进行复用,扩展和修改。

Java对接口允许多实现,但对类只支持单继承,也就是说一个类有且只有一个父类。

【补充:超类】:超类(父类)是被其他类继承的类。它包含共享的属性和行为,子类通过继承超类来复用这些属性和行为。超类通常代表了某一类对象的通用特征,而子类则在此基础上扩展或修改功能。

在这里插入图片描述

超类(父类)(Superclass 或 Base Class):被继承的类,提供共享的属性和方法。
子类(Subclass 或 Derived Class):继承超类的类,拥有超类的属性和方法,同时也可以新增自己的属性和方法。

例子

// 超类(父类)
class Animal {
    String name;

    // 父类构造方法
    public Animal(String name) {
        this.name = name;
    }

    // 父类的方法
    public void speak() {
        System.out.println("动物在叫");
    }

    public void eat() {
        System.out.println("动物在吃东西");
    }
}

// 子类(继承自Animal)
class Dog extends Animal {

    // 子类构造方法,调用父类构造方法
    public Dog(String name) {
        super(name);  // 使用 super 调用父类构造方法
    }

    // 重写父类的方法
    @Override
    public void speak() {
        System.out.println(name + " 说:汪汪");
    }

    // 子类新增方法
    public void bark() {
        System.out.println(name + " 在叫");
    }
}

public class Main {
    public static void main(String[] args) {
        // 创建 Dog 对象
        Dog dog = new Dog("Buddy");

        // 调用父类继承过来的方法
        dog.eat();  // 输出: 动物在吃东西

        // 调用子类重写的方法
        dog.speak();  // 输出: Buddy 说汪汪

        // 调用子类独有的方法
        dog.bark();  // 输出: Buddy 在叫
    }
}

注意

  • 权限修饰符,重写的权限修饰符要大于或等于父类方法的的权限修饰符(public>protected>缺省的>private);

  • final修饰的方法不能被重写;

  • static修饰的方法不能被重写,如果子类的方法也用static修饰,也不构成方法的重写,而是在子类中声明了一个属于子类的静态方法

super关键字

super关键字在 Java 中用于引用父类的成员。它的作用是访问父类的方法和构造函数,类似于 this 关键字,但 this 是指当前对象,而 super 指向父类的对象。使用 super 可以在子类中调用父类的方法,特别是在子类重写了父类的方法时。

主要用法和注意事项:
调用父类的方法:
super 可以用来调用父类的方法。但调用时要注意方法的访问权限,必须确保父类的方法是可访问的(例如,方法的访问修饰符不能是 private,否则无法访问)。

调用父类的构造方法:
在子类的构造函数中,通常会隐式地调用父类的无参构造方法。如果父类没有无参构造函数,子类则必须显式地调用父类的构造方法。调用父类构造方法时,可以使用 super()。例如,如果父类只有带参数的构造函数,子类需要通过 super(参数) 来调用父类的构造方法。

默认继承 Object 类:
在 Java 中,所有类都默认继承自 Object 类。也就是说,如果你没有显式地声明 extends Object,Java 会自动为你添加这一部分。比如,public class Person {} 和 public class Person extends Object {} 是等效的。

方法重写中的 super:
当子类重写了父类的方法后,如果希望调用父类版本的方法,而不是子类重写后的方法,可以使用 super。这在方法重写中尤为常见,尤其当子类需要在保留父类方法的基础上添加自己的行为时。

instanceof(补充)

instanceof是一个双目运算符,运算的结果是一个boolean数据类型,表示是否是某种数据类型。变量 instanceof 某数据类型A 如果变量是A数据类型则返回true,否则返回false。

public class Demo003 {

    public static void main(String[] args) {
        Object i1 = 10;//Integer是一个引用数据类型,表示正数,与int可以相互转换
        System.out.println(i1 instanceof Integer);
        System.out.println(i1 instanceof Object);
        System.out.println(i1 instanceof Teacher);
        System.out.println("-------------------------");
        Teacher teacher = new Teacher();
        System.out.println(teacher instanceof Teacher);
        System.out.println(teacher instanceof Person);
        System.out.println(teacher instanceof Object);
    }

}

结果:

true
true
false
-------------------------
true
true
true

多态

定义

多态是面向对象的一个重要特征,它是指在父类中定义好了属性和方法,子类继承父类之后可以具有不同的数据类型表现或者不同的行为。

同一件事情,发生在不同对象的身上,就会产生不同的结果。核心的一句话:父类引用指向子类对象。

例如:Animal类有makeSound(发出声音)方法,子类Dog和Cat的makeSound方法都有不同的实现,当调用makeSound方法时会根据具体对象的实现而执行。

public class Animal {

    public void makeSound(){
        System.out.println("动物发出的声音");
    }

}
public class Cat extends Animal {

    @Override
    public void makeSound(){
        System.out.println("喵喵喵");
    }

}
public class Dog extends Animal {

    @Override
    public void makeSound(){
        System.out.println("汪汪汪");
    }

}
public class Main {

    public static void main(String[] args) {
        Animal animal = new Animal();
        animal.makeSound();
        Animal dog = new Dog();
        dog.makeSound();
        Animal cat = new Cat();
        cat.makeSound();
    }

}

输出结果:

动物发出的声音
汪汪汪
喵喵喵

转型问题

数据类型的转换包括:向上转型和向下转型
向上转型:向父类转换,可以直接赋值。
向下转型:向子类转换,需要进行强制类型转换。

例子:

public class DataType {

    public static void main(String[] args) {
        /*String数据类型是Object类型的子类*/
        Object o1 = new Object();
        String s1 = "a";
        /*向上转型*/
        Object o2 = s1;
        System.out.println(o2);
        Object oString = "b";
        /*向下转型*/
        String sObject = (String)oString;
        System.out.println(sObject);
        /*向下转型,但是会报错,原因是o1是Object数据,而且存放的数据也不是一个String数据类型的数据*/
        /*强制转换有时候语法上不会报错,但运行时会报错*/
        String s2 = (String)o1;
        System.out.println(s2);
    }

}

数据类型的传递

在不同数据类型的情况下,值的传递是不同的。

基础数据类型

基础数据类型作为参数传递时候,传递的是值,也就说在方法中修改这个参数的值,不会影响到实参的数据。

这个也叫做值传递

public class Type {

    public static void testInt(int a){
        System.out.println("进入方法,在修改值之前:" + a);
        a = 10;
        System.out.println("进入方法,在修改值之后:" + a);
    }

    public static void main(String[] args) {
        int a = -10;
        System.out.println("在进入方法之前:" + a);
        testInt(a);
        System.out.println("在方法执行之后:" + a);
    }

}

结果:

在进入方法之前:-10
进入方法,在修改值之前:-10
进入方法,在修改值之后:10
在方法执行之后:-10

引用数据类型
JDK提供的大多数数据类型,传递的是对象的引用,也就是地址,会修改实参的数据,这个也叫做引用传递。

public class Type {

    public static void main(String[] args) {
        Student student = new Student();
        student.setName("小明");
        student.setAge(10);
        System.out.println("在进入方法之前:" + student.getName() + "的年龄是:" + student.getAge());
        testStudent(student);
        System.out.println("在方法执行之后:" + student.getName() + "的年龄是:" + student.getAge());
    }

    public static void testStudent(Student student){
        System.out.println("进入方法,在修改值之前:" + student.getName() + "的年龄是:" + student.getAge());
        student.setName("大明");
        student.setAge(20);
        System.out.println("进入方法,在修改值之前后:" + student.getName() + "的年龄是:" + student.getAge());
    }

}
public class Student {
    private String name;
    private int age;
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public int getAge() {
        return age;
    }
    public void setAge(int age) {
        this.age = age;
    }
}

结果:

在进入方法之前:小明的年龄是:10
进入方法,在修改值之前:小明的年龄是:10
进入方法,在修改值之前后:大明的年龄是:20
在方法执行之后:大明的年龄是:20

少数:不修改实参数据
String,Integer,Long…

public class Type {

    public static void main(String[] args) {
        String str = "我是原始字符串";
        System.out.println("在进入方法之前:" + str);
        testString(str);
        System.out.println("在方法执行之后:" + str);
    }

    public static void testString(String str){
        System.out.println("进入方法,在修改值之前:" + str);
        str = "我被修改了";
        System.out.println("进入方法,在修改值之后:" + str);
    }
}

结果:

在进入方法之前:我是原始字符串
进入方法,在修改值之前:我是原始字符串
进入方法,在修改值之后:我被修改了
在方法执行之后:我是原始字符串

;