• Java面向对象(封装,继承,多态,接口)


    类和对象

    首先3个问题:

    在这里插入图片描述

    1. 创建一个类

    创建一个学生类,成员变量是姓名和年龄,然后定义构造方法,以及setXxx方法和getXxx方法,分别来赋值和获取。

    public class Student {
        // 定义成员变量
        private String name;
        private int age;
        
        // 空参构造函数
        public Student()
        {
        }
        // 有参构造函数
        public Student(String name,int age)
        {
            this.name = name;
            this.age = age;
        }
        
        /*
        // 针对每一个私有化变量,都要提供get和set方法
        // set方法:给成员变量赋值
        // get方法:对外提供成员变量的值
        */
        
        // 给成员变量name进行赋值
         public void setName(String name)
        {
            this.name = name;
        }
        // 对外提供name属性的值
        public String getName()
        {
            return this.name;
        }
        
         // 给成员变量age进行赋值
        public void setAge(int age) {
            this.age = age;
        }
        // 对外提供age属性的值
        public int getAge() {
            return age;
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42

    举的例子其实就是个标准的javabean类(javabean类:用来描述一类事物的类,不需要写main方法,IDEA中快捷键是Alt+Insert

    2. 创建一个对象

    public class StudentTest{
       public static void main(String[] args){
          // 创建一个Student类对象
          Student std1 = new Student();
          //创建一个带参数的Student类对象
          Student std2 = new Student("zangsan",23);
       }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    3. 访问变量和方法

    public class StudentTest{
       public static void main(String[] args){
          // 创建一个Student类对象
         Student std = new Student();
         std.setName("lisi");
         std.setAge(24);
         
         String name = std.getName();
         int age = std.getAge();
         System.out.println(”姓名:“+name+” “+”年龄:“+age)
       }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    4. some tips

    (1)构造方法
    以三个问题回顾一下构造方法:

    QA
    构造方法的作用?创造对象的时候,由虚拟机自动调用,给成员变量进行初始化的
    构造方法有几种?无参构造方法:初始化的对象时,成员变量的数据均采用默认值。
    有参构造方法:在初始化对象的时候,同时可以为对象进行赋值。
    构造方法的注意事项?(1)任何类定义出来,默认就自带了无参数构造器
    (2)定义了有参构造器,无参数构造器就没有了
    (3)建议在任何时候都手动写上空参和带全部参数的构造方法

    (2)this关键字
    可以区分成员变量和局部变量,与python中的self相似;

    代表方法调用者的地址

    this的内存原理如下:

    在这里插入图片描述

    (3)private关键字

    • private关键字是一个权限修饰符,可以修饰成员(成员变量和成员方法);
    • 被private修饰的成员只能在本类中才能访问
    • 针对private修饰的成员变量,如果需要被其他类使用,提供相应操作,如:提供setXxx(参数)方法**,用于给成员变量赋值,方法用public修饰;提供getXxx()方法,用于获取成员变量的值,方法用public修饰。

    封装

    面向对象三大特性:封装,继承,多态。

    原则:对象代表什么,就得封装对应的数据,并提供数据对应的行为

    在这里插入图片描述

    如何实现封装?

    • 修改属性的可见性,在属性的前面添加修饰符 (private)
    • 对每个值属性提供对外的公共方法访问,如创建 getter/setter(取值和赋值)方法,用于对私有属性的访问
    • 在 getter/setter 方法里加入属性的控制语句,例如我们可以加一个判断语句,对于非法输入给予否定

    修饰符

    1. 访问控制修饰符

    在这里插入图片描述

    在实际中,一般只用private 和 public,按照 成员变量私有方法公开的原则

    2. 非访问修饰符

    (1)static

    分为静态变量和静态方法

    • 静态变量
      static 关键字用来声明独立于对象的静态变量。无论一个类实例化多少对象,它的静态变量只有一份拷贝,也就是说被该类所有对象共享, 静态变量也被称为类变量。可以直接通过类名调用。

    • 静态方法
      static 关键字用来声明独立于对象的静态方法。静态方法不能使用类的非静态变量。静态方法从参数列表得到数据,然后计算这些数据。

    具体可以看下图:

    在这里插入图片描述

    (工具类:帮助我们做一些事情,但是不具体描述任何事物的类
    特点:类名知名见意;私有化构造方法)

    最后说一下static的注意事项

    在这里插入图片描述

    而main函数的修饰符static,其实也是一样的道理

    在这里插入图片描述

    (2)final

    • final 修饰方法:表明该方法是最终方法,不能被重写;
    • final 修饰类:表明该类是最终类,不能被继承;
    • final 修饰变量:即常量,只能被赋值一次。

    注意:

    • final修饰的变量是基本类型:那么变量存储的数据值不能发生改变。
    • final修饰的变量是引用类型:那么变量存储的地址值不能发生改变,对象内部的可以改变。

    继承

    关键词:extend

    class 父类 {
    }
     
    class 子类 extends 父类 {
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5

    1. 继承的类型

    (1)多重继承(直接父类,间接父类)

    在这里插入图片描述
    代码:

    class B extends A{
    }
    
    class C extends B{
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5

    (2)不同类继承同一个类(一个父亲可以有多个儿子)

    在这里插入图片描述

    代码:

    class B extends A{
    }
    
    class C extends A{
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5

    (3)多继承不支持,即一个类继承两个类不支持(一个儿子不能有两个爸爸)

    总结一下:

    • Java只能单继承:一个类只能继承自一个直接父类(每个类只有一个爸爸)。
    • Java不支持多继承、但是支持多重继承(爷父子三代)。
    • Java中所有的类都直接或者间接继承于Object类。(所有类都有爸爸)

    2. 继承的特性

    (1)子类拥有父类非 private 的属性、方法

    (2)子类可以拥有自己的属性和方法,即子类可以对父类进行扩展。

    (3)子类可以用自己的方式实现父类的方法。

    子类能继承父类的哪些内容
    在这里插入图片描述
    注意:

    (1)private的私有变量虽然可以继承,但是子类不能使用

    (2)虚方法表:父类会将“经常用的方法”放入虚方法表中,并且这些方法是非private,非static,非final的。然后子类继承后,会在父类基础上添加自己类的虚方法。

    在这里插入图片描述

    3. 继承中的访问特点:

    this调用:就近原则 ;super调用:直接找父类

    (1)成员变量:就近原则
    先在局部位置找——本类成员位置找——父类成员位置找,逐级往上。

    如果子类父类中出现不重名的成员变量,这时的访问是没有影响的

    子父类中出现了同名的成员变量时,子类会优先访问自己对象中的成员变量

    在这里插入图片描述
    (2)成员方法:与成员变量类似,也是就近原则

    (3)构造方法:

    • 子类不能继承父类的构造方法,但是可以通过super调用
    • 子类构造方法的第一行,有一个默认的super();
    • 默认先访问父类中无参的构造方法,再执行自己。
    • 如果想要访问父类有参构造,必须手动书写。

    eg:

    // 创建一个Person类
    class Person()
    {
       private String id;
       private String name;
    
      public Person(String id,String name)
      {
      this.id = id;
      this.name = name;
      }
    }
    // 创建一个Student类,继承自Person类
    class Student extends Person()
    {
    	private String gender;
    	public Student(String id,String name,String gender)
    	{
    	super(id,name);
    	this.gender = gender;
    	} 
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22

    4. super&this

    super关键字:我们可以通过super关键字来实现对父类成员的访问,用来引用当前对象的父类。

    this关键字:指向自己的引用

    在这里插入图片描述

    具体地:

    super 关键字在子类内部使用,代表父类对象。

    • 访问父类的属性 super.属性名。
    • 访问父类的方法 super.bark()。
    • 子类构造方法需要调用父类的构造方法时

    5. 重写(Override)&重载(Overload)

    重写(Override):
    是子类对父类的允许访问的方法的实现过程进行重新编写, 返回值和形参都不能改变。即外壳不变,核心重写!

    重写的好处在于子类可以根据需要,定义特定于自己的行为。 也就是说子类能够根据需要实现父类的方法。

    特点:
    (1)重写方法的名称、形参列表必须与父类中的一致。

    (2)子类重写父类方法时,访问权限子类必须大于等于父类

    (3)子类重写父类方法时,返回值类型子类必须小于等于父类

    (4)建议:重写的方法尽量和父类保持一致。

    (5)只有被添加到虚方法表中(前面说的三个非)的方法才能被重写

    重载(overload)
    在一个类里面,方法名字相同,而参数不同。返回类型可以相同也可以不同。

    每个重载的方法(或者构造函数)都必须有一个独一无二的参数类型列表。最常用的地方就是构造器的重载。

    • 被重载的方法必须改变参数列表(参数个数或类型不一样);
    • 被重载的方法可以改变返回类型;
    • 被重载的方法可以改变访问修饰符;

    总结一句话:

    重写是父类与子类之间多态性的一种表现,重载可以理解成一个类多态的具体表现形式。

    多态

    同一个事件发生在不同的对象上会产生不同的结果(或者说是同类型的对象,表现出的不同形态)

    1. 三个前提条件

    • 有继承/实现关系
    • 父类引用指向子类对象Fu fu = new Zi()
    • 有方法重写

    通俗地讲,多态就是只通过父类就能够引用不同的子类

    2. 多态的成员特点

    调用成员变量:编译看左边,运行也看左边

    • 编译看左边: javac编译代码的时候,会看左边的父类中有没有这个变量,如果有,编译成功,如果没有编译失败。
    • 运行也看左边: java运行代码的时候,实际获取的就是左边父类中成员变量的值

    调用成员方法:编译看左边,运行看右边

    • 编译看左边:javac编译代码的时候,会看左边的父类中有没有这个方法,如果有,编译成功,如果没有编译失败。
    • 运行看右边: java运行代码的时候,实际上运行的是子类中的方法

    简单理解一下:

    Fu f = new Zi()//编译看左边的父类中有没有name这个属性,没有就报错
    //在实际运行的时候,把父类name属性的值打印出来
    System.out.println(f.name);
    
    //编译看左边的父类中有没有show这个方法,没有就报错
    //在实际运行的时候,运行的是子类中的show方法
    f.show();
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8

    完整示例:

    public class Animal {
        public String name = "LaoWang";
        public void eat()
        {
            System.out.println("动物吃食物");
        }
    }
    
    public class Cat extends Animal {
        public String name = "XiaoWang";
        // 注意这个catMouse是特有方法
        public void catMouse()
        {
            System.out.println("猫抓老鼠");
        }
        // eat是重写方法
        public void eat()
        {
            System.out.println("猫吃骨头");
        }
    }
    
    public class Test {
        public static void main(String[] args) {
            Animal c = new Cat();
            System.out.println(c.name);   // 成员变量:编译看左边,运行也看左边
            c.eat();  // 成员方法:编译看左边,运行看右边
            c.catMouse() // 编译出错
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28

    输出结果:

    LaoWang
    猫吃骨头

    3. 多态的优缺点

    好处:

    (1)右边对象可以解耦,便于拓展和维护

    Animal a = new Dog();
    a.eat(); // 业务逻辑发生改变时,后续代码无需改变
    
    • 1
    • 2

    (2)使用父类型作为参数,可以接收所有子类对象,体现多态的拓展性和便利性

    弊端

    (1)不能调用子类的独有方法

    如果需要调用该怎么办?

    引用数据类型的类型转换——向下转型

    Animal a = new Dog();
    Dog d = (Dog) a;
    
    • 1
    • 2

    这样转换成了真正的子类类型,从而可以调用子类的独有功能。(转换类型与真实对象类型不一致会报错)

    所以更多时候,可以加一个判断语句:instanceof

    public static void show(Animal a)
    {
            a.eat();
            // 类型判断
            if (a instanceof Cat)  {  // 猫做的事情 
                Cat c = (Cat)a;
                c.catMouse();
            } else if (a instanceof Dog) { // 狗做的事情 
                Dog c = (Dog)a;
                c.lookHouse();
            }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12

    抽象类和抽象方法

    1. 含义

    • 抽象方法:将共性的行为(方法)抽取到父类之后,由于每一个子类执行的内容是不一样,所以,在父类中不能确定具体的方法体。该方法就可以定义为抽象方法。

    • 抽象类:一个类中没有包含足够的信息来描绘一个具体的对象。如果一个类中存在抽象方法,那么该类就必须声明为抽象类

    2. 定义格式——abstract

    抽象方法只包含一个方法名,而没有方法体,方法名后面直接跟一个分号,而不是花括号;

    抽象类同样也用 abstract 修饰

    //抽象方法
    public abstract 返回值类型 方法名(参数列表);
    
    //抽象类
    public abstract class 类名{}
    
    • 1
    • 2
    • 3
    • 4
    • 5

    3. 特性

    • 抽象类不能实例化,所以抽象类必须被继承
    • 抽象类中不一定有抽象方法,有抽象方法的类一定是抽象类,可以有构造方法
    • 抽象类的子类:要么重写抽象类中的所有抽象方法 ;要么是抽象类

    4. 举个栗子

    // 父类,抽象类
    abstract class Employee {
    	private String id;
    	private String name;
    	
    	public Employee() {
    	}
    	
    	public Employee(String id, String name) {
    		this.id = id;
    		this.name = name;
    	}
    	
    	// 抽象方法必须要放在抽象类中
    	public abstract void work();
    }
    
    // 定义一个子类继承抽象类
    class Manager extends Employee {
    	public Manager() {
    	}
    	public Manager(String id, String name) {
    		super(id, name);
    	}
    	
    	@Override 重写父类的抽象方法
    	public void work() {
    		System.out.println("管理其他人");
    	}
    }
    
    // 定义一个子类继承抽象类
    class Cook extends Employee {
    	public Cook() {
    	}
    	public Cook(String id, String name) {
    		super(id, name);
    	}
    	@Override
    	public void work() {
    		System.out.println("厨师炒菜多加点盐...");
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29
    • 30
    • 31
    • 32
    • 33
    • 34
    • 35
    • 36
    • 37
    • 38
    • 39
    • 40
    • 41
    • 42
    • 43

    接口

    1. 什么是接口

    首先为什么要有接口?

    从下图可以看出,动物类按照之前讲的,可以定义为抽象类,里面有“吃饭喝水”抽象方法,这时下面的子类都需要重写这两个方法。但是,”游泳“方法只有“青蛙和狗类”可以重写,所以将其定义为一个接口。

    什么是接口?

    • 接口用于描述类所具有的功能,而不提供功能的实现
    • 接口中的方法是抽象方法(没有方法体)
    • 接口中的方法体需要写在实现接口的类中,并且该类必须实现接口中所有抽象方法。(除非实现接口的类是抽象类)
    • 接口不能实例化,没有构造函数

    2. 如何定义和使用一个接口

    定义一个接口——interface:

    // 关键字:interface
    public interface A{ }
    
    • 1
    • 2

    使用(实现)一个接口——implements:

    // 关键字:implements
    public class C implements A{}
    
    • 1
    • 2

    注:接口的继承:可以单继承,也可以多继承

    单继承:

    public interface Sports
    {
       public void setHomeTeam(String name);
    }
    // 文件名: Football.java
    public interface Football extends Sports
    {
       public void homeTeamScored(int points);
       public void visitingTeamScored(int points);
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10

    此时当类实现Football接口时,需要实现它及它的父接口的所有方法,所以共需要实现2+1=3个方法

    多继承:

    public interface inter1
    {
       public void method1();
    }
    
    public interface inter2
    {
       public void method2();
    }
    
    public interface inter3 extends inter1,inter2
    {
       public void method3();
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14

    那么,在类实现接口inter3的时候,需要重写所有抽象方法(1+1+1=3个)

    3. 接口里成员的特点

    • 成员变量:只能是常量,会被隐式的指定为 public static final 变量
    • 成员方法:隐式抽象的,接口中的方法会被隐式的指定为 public abstract
    • 构造方法:没有

    注意:
    JDK 1.8 以前:接口只能定义抽象方法
    JDK 1.8 以后:接口中可以定义有方法体的方法;
    JDK 1.9 以后:接口中可以定义私有方法

    eg.

    // 定义接口A,提供eat方法接口,抽象方法
    public interface A{
       
    	public abstract void eat();
    }
    // 定义接口 B,提供 sleep 方法接口,抽象方法
    public interface B{
    	public abstract void sleep();
    }
    //定义类 C 实现 A,B 方法的接口
    public class C implements A,B{
    	public void eat()
    	{
    	   System.out.println("吃饭");
    	}
    	public void sleep()
    	{
    	  System.out.println("睡觉");
    	}
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20

    4. 接口与类的关系

    类和接口的关系?

    • 接口并不是类,类描述对象的属性和方法。接口则包含类要实现的方法。
    • 类只能单继承不可以多继承,但接口可以多继承
    • 接口无法被实例化,但是可以被实现。一个实现接口的类,必须实现接口内所描述的所有方法,否则就必须声明为抽象类。

    抽象类与接口的区别

    • 抽象类中的方法可以有方法体,但是接口中的方法不行。
    • 抽象类中的成员变量可以是各种类型的,而接口中的成员变量只能是 public static final 类型的。
    • 接口中不能含有静态代码块以及静态方法(用 static 修饰的方法),而抽象类是可以有静态代码块和静态方法。
    • 一个类只能继承一个抽象类,而一个类却可以实现多个接口。

    内部类

    内部类就是定义在一个类里面的类,里面的类可以理解成(寄生),外部类可以理解成(宿主)

    public class People
    {
    // 内部类
    public class Heart {
    }
    }

    场景:当一个事物的内部,还有一个部分需要一个完整的结构进行描述时,就可以采用内部类(汽车类,发动机类作为内部类)

    特点:

    • 内部类通常可以方便访问外部类的成员,包括私有的成员
    • 内部类提供了更好的封装性,内部类本身就可以用private ,protectecd等修饰,封装性可以做更多控制

    内部类一共有4种,在后面的学习中,主要涉及匿名内部类

    1. 静态内部类

    2. 成员内部类

    3. 局部内部类

    4. 匿名内部类

    (1)从基本概念出发

    什么是匿名内部类?

    • 隐藏了名字的内部类,可以写在成员位置,也可以写在局部位置
    • 包含了 继承或者实现接口,方法重写,创建对象
    • 整体就是一个类的子类对象或者接口的实现类对象

    匿名内部类的作用?
    方便创建子类对象,最终目的是为了简化代码编写

    匿名内部类的格式?

    new 类名或接口名(){
    重写方法
    };

    Employee a = new Employee() 
    { 
      // 重写方法  
       public void work() 
       {   }
    };
    a.work();
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7

    当方法的参数是接口或者类时,可以传递这个接口或类的实现类对象

    (2)从实际场景出发

    上面讲了那么多,可能比较枯燥,直接上代码来解释下:

    e.g.1
    在之前我们要实现一个接口一般这么做:

    // 先定义一个接口
    interface inter{
        public void eat();
    }
    
    // 写一个类去实现接口inter
    class A implements inter{
        @Override
        public void eat() {
            System.out.println("正在调用eat方法");
        }
    }
     // main方法中创建类A的对象,并调用方法
    public static void main(String[] args) {
            A a = new A();
            a.eat();  // 正在调用eat方法
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    如果我们只是想单纯的使用一次eat方法,不需要创建对象的话,就可以用匿名类

    // 首先还是创建一个接口
    interface inter{
        public void eat();
    }
    
    
    public class AnonymousDemo
    {
    	public static void main(String[] args) {
    	// 直接使用匿名内部类,调用eat方法
            new inter(){
                @Override
                public void eat() {
                    System.out.println("正在调用eat方法");
                }
            }.eat();
        }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18

    e.g.2 :对象是方法的参数

    // 先定义一个接口
    interface swimming()
    {
       void swim();
    }
    
    public static void main(String[] args) {
       Swimming s = new Swimming()
       {
       		public void swim(){
               System.out.println("学生快乐的游泳");
            }
       };
       go(s);
       
       /* 可以直接写成
        go(new Swimming()
           {
       		public void swim(){
               System.out.println("学生快乐的游泳");
            }
       };)
       */
       
       public void go(Swimming s)
       {
       s.swim(); 
       }
    }
    
    • 1
    • 2
    • 3
    • 4
    • 5
    • 6
    • 7
    • 8
    • 9
    • 10
    • 11
    • 12
    • 13
    • 14
    • 15
    • 16
    • 17
    • 18
    • 19
    • 20
    • 21
    • 22
    • 23
    • 24
    • 25
    • 26
    • 27
    • 28
    • 29

    参考链接:
    1.https://www.bilibili.com/video/BV17F411T7Ao?p=131&share_source=copy_web&vd_source=fa075f0e5dab81ef7c98b9eb4c47d9a7
    2. https://www.runoob.com/java/java-tutorial.html
    3. https://blog.csdn.net/xueyukun1/article/details/121412738

  • 相关阅读:
    python聚类分析如何可视化?
    算法 缺失的第一个正整数-(哈希)
    基于大规模测量和多任务深度学习的电子鼻系统目标识别、浓度预测和状态判断
    ubuntu(18.04)中安装open babel docker镜像并在php项目中调用容器中的obabel命令解析结果使用
    SpringBoot图片文件上传
    MS14-068 漏洞分析—不安全的PAC
    不同的测试技术区分
    C语言之预处理命令使用详解----#if、#endif、#undef、#ifdef、#else、#elif
    编写竞赛程序
    高校教务系统登录页面JS分析——四川大学
  • 原文地址:https://blog.csdn.net/ji_meng/article/details/126147968