2

面向对象之封装、继承、多态

封装

将类中的信息进行隐藏在类的内部,不允许外部程序信息直接访问,而是只能通过该类的提供的方法来实现对隐藏信息的提供和访问。
  • 好处
只能隐藏规定的方法和数据
隐藏类的实现细节,方便修改和实现

封装的实现步骤

1. 用private等修饰符修饰属性
2. 创建属性的getter/setter方法
3. 在getter/setter方法中加入属性的控制语句

java中的访问修饰符

clipboard.png

java中的访问修饰符一个有四个,我们主要学习privateprotectedpublic三个。

java中的this关键字

this关键字代表当前对象

this.属性 操作当前对象的属性
this.方法 操作当前对象的方法

封装对象的属性的时候会经常使用this关键字

package com.test.cat; // 定义包

public class Cat {
    int a;
    int b;

    public void send() {
        System.out.print("send");
    }

    public int getA() {
        this.send(); // 调用方法
        return a;
    }
    public void setA(int a) {
        this.a = a;  // 调用属性
    }
    public int getB() {
        return b;
    }
    public void setB(int b) {
        this.b = b;
    }
    public Cat() {
        System.out.println("Cat 实例化了");
    }
}

java中的内部类

内部类( Inner Class )就是定义在另外一个类里面的类。与之对应,包含内部类的类被称为外部类。

内部类的主要作用

1. 内部类提供了更好的封装,可以把内部类隐藏在外部类之内,不允许同一个包中的其他类访问该类
2. 内部类的方法可以直接访问外部类的所有数据,包括私有的数据
3. 内部类所实现的功能使用外部类同样可以实现,只是有时使用内部类更方便

内部类的分类

成员内部类
静态内部类
方法内部类
匿名内部类
  • 先举个例子
//外部类HelloWorld
public class HelloWorld {
    
    // 内部类Inner,类Inner在类HelloWorld的内部
    public class Inner {
        
        // 内部类的方法
        public void show() {
            System.out.println("welcome to imooc!");
        }
    }
    
    public static void main(String[] args) {
        
        // 创建外部类对象
        HelloWorld hello = new HelloWorld();
        // 创建内部类对象
        Inner i = hello.new Inner();
        // 调用内部类对象的方法
        i.show();
    }
}
java中的成员内部类(普通内部类)

clipboard.png

package innerClass;
 
// 外部类
public class Outer {
    private int out; // 外部类成员
    private int b; // 外部的b
    
    public int getOut() {
        return out;
    }
    public void setOut(int out) {
        this.out = out;
    }
    // 外部类构造函数
    public Outer(int _out) {
        this.out = _out;
        System.out.println("外部类实例化中.....");
        // 外部类则无法访问内部类的数据
    }
    
    // 内部类
    public class Inner{
        private int in; // 内部类成员
        private int b; // 内部的b
        
        // 内部类构造函数
        public Inner(int _in) {
            this.in = _in;
            System.out.println("内部类实例化中....");
            System.out.println(out); // 内部类可以访问外部类中的任何数据
            System.out.println(this.b); // 如果遇上重名,那么默认是使用内部的变量,如果想要使用外部的变量,可以使用this 
        }
    }
    
    public static void main(String [] args) {
        Outer o = new Outer(100);  // 创建一个外部对象
        System.out.println(o.getOut());
        
        Inner i = o.new Inner(200); // 创建一个内部对象
    }
}
  • 注意事项
1. Inner 类定义在 Outer 类的内部,相当于 Outer 类的一个成员变量的位置,Inner 类可以使用任意访问控制符,如 public 、 protected 、 private 等
2. Inner 类中定义的 test() 方法可以直接访问 Outer 类中的数据,而不受访问控制符的影响
3. 定义了成员内部类后,必须使用外部类对象来创建内部类对象,而不能直接去 new 一个内部类对象,即:内部类 对象名 = 外部类对象.new 内部类( );
4. 编译上面的程序后,会发现产生了两个 .class 文件
其中,第二个是外部类的 .class 文件,第一个是内部类的 .class 文件,即成员内部类的 .class 文件总是这样:外部类名$内部类名.class
  • 外部类是不能直接使用内部类的成员和方法滴

clipboard.png

  • 如果外部类和内部类具有相同的成员变量或方法,内部类默认访问自己的成员变量或方法,如果要访问外部类的成员变量,可以使用 this 关键字。

clipboard.png


Java 中的静态内部类
静态内部类是 static 修饰的内部类
  • 特点
1. 静态内部类不能直接访问外部类的非静态成员,但可以通过 new 外部类().成员 的方式访问 
2. 如果外部类的静态成员与内部类的成员名称相同,可通过“类名.静态成员”访问外部类的静态成员;如果外部类的静态成员与内部类的成员名称不相同,则可通过“成员名”直接调用外部类的静态成员
3. 创建静态内部类的对象时,不需要外部类的对象,可以直接创建 内部类 对象名= new 内部类();
package static_inner_class;

public class Outer {
    private int a; // 外部普通成员变量
    static int b = 10; //外部静态变量
    
    public Outer(int _a) {
        this.a = _a;
        System.out.println("外部类实例化中....");
    }
    
    
    // 内部类
    public static class Inner{
        private int b;
        public Inner(int _b) {
            this.b = _b;
            System.out.println("内部类实例化中....");
        }
        
        public void test() {
            System.out.println("访问外部类中的b:" + Outer.b); 
            // 访问外部静态变量,建议使用class.staticName ,即使这是处理外部类和内部类存在同名变量的情况下
            System.out.println("访问内部类中的b:" + b); // 访问内部变量可以直接访问
        }
    }
    
    public static void main(String [] args) {
        Outer o = new Outer(10);
        Inner i = new Inner(20); // 创建内部类可以直接使用该方法,不用先实例化外部类
        i.test();
    }
}
Java 中的方法内部类
方法内部类就是内部类定义在外部类的方法中,方法内部类只在该方法的内部可见,即只在该方法内可以使用。
//外部类
public class HelloWorld {
    
    private String name = "爱慕课";
    
    // 外部类中的show方法
    public void show() { 
        // 定义方法内部类
        class MInner {
            int score = 83;
            public int getScore() {
                return score + 10;
            }
        }
        
        // 创建方法内部类的对象
        MInner mi=new MInner();
        
        // 调用内部类的方法
        int newScore=mi.getScore();
        
        System.out.println("姓名:" + name + "\n加分后的成绩:" + newScore);
    }
    
    // 测试方法内部类
    public static void main(String[] args) {
        
        // 创建外部类的对象
        HelloWorld mo=new HelloWorld();
        
        // 调用外部类的方法
        mo.show();
    }
}

继承

继承就是类与类之间的一种 is a的关系

clipboard.png

java中继承是单继承的,也就是只能由一个父类

  • 好处
1. 实现代码的复用
2. 子类拥有父类的所有属性和方法
3. 但是private修饰的属性和方法却无法访问到
  • 语法规则

clipboard.png

例子
// Animal.java

package inhert_demo1;

public class Animal {
    public int age;
    public String name;
    public void eat() {
        System.out.println("动物可以吃东西");
    }
}
// Cat.java
package inhert_demo1;

public class Cat extends Animal{
    // 【方法的重写】
    public void eat() {
        System.out.println("猫咪可以吃东西");
    }
}
// test.java

package inhert_demo1;

public class test {
    public static void main(String [] args) {
        Cat c = new Cat();
        c.eat();
    }
}

方法的重写

如果子类对继承父类的方法不满意,是可以重写父类的方法的,当调用的时候,优先调用子类的方法
// 规定
1. 返回值相同
2. 参数类型及个数相同
3. 方法名相同

继承的出发顺序

1. 先初始化父类,然后再初始化子类
2. 先执行初始化对象中的属性,然后再执行构造函数中的初始化
// 执行顺序为
package inhert_demo1;

public class Animal {
    public int age = 10; //【1】 属性初始化
    public String name;
    
    public Animal() {
        age = 20; // 【2】构造函数中的初始化
    }
    public void eat() {
        System.out.println("动物可以吃东西");
    }
}

clipboard.png

final "最终的"

final可以修饰类、属性、方法、变量
final修饰的类,则该类不能被继承
final修饰的方法,则方法不允许被覆盖(也就是方法的重写)
final修饰的属性: 属性初始化必须是在属性初始化或者构造方法中初始化,只能人选其一
final修饰的变量,这个变量就成为了常量,只能赋值一次

super

super关键字代指父类 (在对象内部使用、可以代表父类对象)

clipboard.png

// 子类 cat
package inhert_demo1;

public class Cat extends Animal{
    public int age = 20;
    public void eat() {
        System.out.println("猫咪可以吃东西");
    }
    
    public void meyhod () {
        // super代表父类
        System.out.println(super.age); // 使用父类的属性
        super.eat(); // 子类中调用父类的方法
    }
}
  • 注意
1. 如果子类中的构造方法没有显示的调用父类的构造方法,那么系统将默认调用父类的无参构造方法

也就是会隐世的调用`super()`

2. 如果父类没有无参构造方法,而是有有参的构造方法,那么子类中就必须显示的调用父类的构造方法。

Object类

Object类是所有类的父类,如果一个类没有明确的使用extends来指定继承一个类,那么这个类默认继承Object(Object类中的方法适合子类)

方法一、toString()

Object中的类方法toString()返回的是一个对象的哈希Code码(对象的地址引用)

// 我们则是想使用该方法来返回属性的值,因此我们可以重写该方法

// cat类中
public String toString() {
    return "Cat [age=" + age + "]";
}


//使用

package inhert_demo1;

public class test {
    public static void main(String [] args) {
        Cat c = new Cat();
        System.out.println(c.toString());
    }
}

方法二、equals()

比较对象的引用是否指向同一块内存地址。
Dog d1 = new Dog();
Dog d2 = new Dog();

d1.d2是两个地址,是不相同的

// 如果我们想要用equals来比较两个对象的值是否相同,这就需要我们重写该方法
// cat中重写

    @Override
    public boolean equals(Object obj) {
        if (this == obj)
            return true;
        if (obj == null)
            return false;
        if (getClass() != obj.getClass())// getClass 获取到的是类对象
            return false;
        Cat other = (Cat) obj;
        if (age != other.age)
            return false;
        return true;
    }
Cat c = new Cat();
System.out.println(c.toString());
Cat c1 = new Cat();
if(c1.equals(c)) {
    System.out.println("两个对象的属性相同");
}
  • 补充 getClass 获取到的是类对象

clipboard.png

多态

多态指的是对象的多种形态
1. 引用多态
   父类的引用可以指向子类的对象
   父类的引用可以指向本类的对象
public static void main(String [] args) {
    Animal d1 = new Animal();
    Animal d2 = new Dog();
    Dog d3 = new Animal(); // 报错了,因为是子类的引用指向了父类的对象
}
2. 方法多态
    创建本类对象时,调用的时本类的方法
    创建子类对象时,调用的方法时子类重写的方法或者时父类继承过来的方法
    Animal d1 = new Animal();
    Animal d2 = new Dog();
    d1.eat(); // 调用父类的eat方法
    d2.eat(); // 调用子类重写的eat方法

多态中的引用类型转换

1. 向上类型转换(隐式/自动类型转换) 小类型 => 大类型
2. 向下类型转换(强制类型转换) 大类型 => 小类型
3. instanceof可以解决引用对象的类型,来避免转换类型的安全性问题

clipboard.png

package polymorphic;

public class test {
    public static void main(String [] args) {
        Dog dog = new Dog();
        Animal animal = dog;  // 小类型 => 大类型
//        Dog dog2 = animal;    // 报错了,因为大类型 => 小类型 存在风险
        Dog dog2 = (Dog)animal; // 强制类型转换,就不报错了
//        Cat cat =(Cat) animal; // 编译时Cat类型,运行时时Dog类型
        
        if(animal instanceof Dog) {
            
        } else {
            
        }
        
    }
}

抽象类

使用abstract修饰的类成为抽象类
1. 某一个父类只知道子类应该含有怎样的方法,但是无法确定这个子类的方法如何实现
2. 从多个具有相同特征的类中抽象出一个抽象类,以这个抽线类作为子类的模板,从而避免了子类设计的随意性
  • 使用规则
abstract定义抽象方法,只有声明、不需要实现
抽象类中可以有普通方法,也可以有抽象方法
抽象类不能直接创建,我们可以定义引用变量
public abstract class telphone {
    // 定义抽象方法
    public abstract void call();
    public abstract void message();
    // 抽象方法没有方法体,因此以分号结束
}

实例

// Telphone.java
package abstrated;

public abstract class Telphone {
    // 定义抽象方法
    public abstract void call();
    public abstract void message();
    // 抽象方法没有方法体,因此以分号结束
}
// Cellphone.java

package abstrated;

public class Celltelphone extends Telphone {
    public void call() {
        System.out.println("通过键盘打电话");
    }
    public void message() {
        System.out.println("通过键盘发短信");
    }
    
}
// smartphone.java
package abstrated;

public class Smarttelphone extends Telphone{
    public void call() {
        System.out.println("通过语音打电话");
    }
    public void message() {
        System.out.println("通过语音发短信");
    }
}
// initial.java
package abstrated;

public class initial {

    public static void main(String[] args) {
        // TODO Auto-generated method stub
        Telphone t1 = new Celltelphone(); // 使用父类的引用创建子类对象
        t1.call();
        t1.message();
        
        Telphone t2 = new Smarttelphone(); // 使用父类的引用创建子类对象
        t2.call();
        t2.message();
    }

}

clipboard.png

接口

接口可以理解为时一种特殊的类,由全局常量和公共的抽象方法来组成

它是类的一种具体实现,而接口定义了某一批类所需要遵守的【规范】,接口并不关心这些类的内部实现,也不关心这些类的实现细节,只是规定了这些类的必须实现的某些方法。

接口定义

clipboard.png

clipboard.png

clipboard.png

clipboard.png

clipboard.png

interface A{//定义一个接口

    public static final String MSG = "hello";//全局常量
    public abstract void print();//抽象方法
}
// X.java

package interfaceDemo1;

interface A{
    public static final String MSG = "hello"; // 全局常量
    public abstract void print(); // 抽象方法
}

interface B{
    public static final int a = 10;
    public abstract void get();
}

public class X implements A,B{
    // X实现了两个接口
    public void print() {
        System.out.println("接口A的抽象方法print()");
    }
    
    public void get() {
        System.out.println("接口B的抽象方法get()");
    }
    
}
// test.java
package interfaceDemo1;

public class test {

    public static void main(String[] args) {
        // TODO Auto-generated method stub
        X x = new X(); // 实例化子类
        A a = new X();
        B b = new X();
        
        x.print();
        x.get();
        a.print();
        b.get();
    }

}
未完待续

Meils
1.6k 声望157 粉丝

前端开发实践者