JAVA的封装/继承/多态三大特性

封装、继承、多态是面向对象的语言的共同特性,不止JAVA。

目录

封装

继承

继承正文

子类的构造方法

super

方法重写 

多态 

向上转型和向下转型

静态绑定、动态绑定

多态正文


封装

封装顾名思义就是封藏一个事物的内在属性、具体细节等,而JAVA里的封装是选择性的将一个类的一些属性、方法用private修饰符修饰达到封装目的,被private修饰后,被修饰的属性/方法仅能在本类中使用。

public class Test {
    private int age;
    private public void func(){
        System.out.println("这是父类封装的方法"); 
    }

}
class Test1 extends Test{
    public static void main(String[] args) {
        Test1 te=new Test1();
        System.out.println(te.age);//1
        te.func();//2
    }
}
//Test1继承自Test,也继承了age和func(),却不能被子类来使用,1、2都会编译错误

若想不通过引用Test类的对象来访问被封装的内容,可以通过设置get和set类方法来访问和修改成员变量,也可以通过其他类方法来间接访问private权限的方法。例如:

public class Test {
    private int age;
    private void func(){
        System.out.println("这是父类封装的方法");
    }

    public void setAge(int age) {
        this.age = age;
    }
    public int getAge() {
        return age;
    }

    public void control(){
        func();
    }
}
class Test1 extends Test{
    public static void main(String[] args) {
        Test1 te=new Test1();
        te.setAge(99);//修改父类private权限的age的值
        System.out.println(te.getAge());//得到age的值
        te.control();//间接调用了func()
    }
}

继承

继承正文

 当我们需要创建一些具有相同属性和特征的类时,如抽象建立红毛猩猩类、狒狒类、金丝猴类。它们都有毛发、吃饭等共同特征,而一个一个重复创建太浪费代码空间,这时候我们就需要使用到继承。继承关系中分父类(也称基类、超类)和子类(派生类),关键字:extends。子类将继承父类定义的所有字段、类方法,只要不是private权限的字段、类方法,子类创建的对象都可以使用,子类还可以定义自己的字段、方法,可以说子类是父类的扩展。

public class Monkey {
    private int age;
    String color;
    public void eat(){
        System.out.println("灵长类都会吃饭。。。");
    }
}
class Monkey1 extends Monkey{
    public static void main(String[] args) {
        Monkey1 monkey=new Monkey1();
//        System.out.println(monkey.age);这里将会编译错误,
//        子类引用的对象不可访问父类private的age,即便子类继承了这个private的age
        monkey.color="red";
        monkey.eat();
    }
}

注意:

  1. 当子类引用的对象使用成员变量、调用方法时,先在子类中找,再去父类找。
  2. 若想在子类定义与父类重名的成员变量/类方法,如果不用super来显式调用,子类引用的对象访问它们时,优先去子类寻找。子类定义与父类同名的类方法时,不可将返回类型改变而不改参数列表,因为这样定义重名方法的操作是遵从方法重载的规则的。
  3. 一个父类可以被多个子类继承,但一个子类不可继承多个父类,理论上父类可被子类继承、子类又被继承、子类的子类被继承........,但这样在十几种并不合理,如果一个类不想被继承,可使用final关键词修饰。
class A{
}
 class B{
}
class C extends A,B{
}//非法的继承操作
——————————————————————
public final class Monkey {
    /*代码.......*/
}
class Monkey1 extends Monkey{//Monkey已被final修饰,再继承将会编译错误

}

子类的构造方法


super

super是子类显式访问父类成员变量、类方法的关键字,主要用于在子类里对父类成员变量的初始化,当子类有与父类同名的成员变量、方法时,可以使用super来直接访问父类的变量、方法。


通常有以下几种构造情况:(如果父类自己定义了无参构造方法,编译器不会自动提供无参构造方法,super()调用的也将是自己定义的无参构造,需要注意:super构造必须是子类构造方法的第一句

  1. 当父类和子类都未定义构造方法的情况,编译器自动生成这两种构造方法:
    public class Monkey {
        /*如果定义有父类成员变量*/
        public Monkey(){
            /*如果有成员变量,将会赋默认值*/
        }
    }
    class Monkey1 extends Monkey{
        /*如果定义有子类成员变量*/
        public Monkey1(){
            super();
            /*子类成员变量的赋值操作,同样赋默认值*/
       

    a.父类未定义构造方法,而子类定义了有/无参构造,编译器自动提供父类的无参构造进行初始化。b.父类定义了无参构造,子类未定义构造方法,编译器提供子类的构造方法,并且第一句为super();。

  2. 父类未定义有参构造,子类定义了构造方法(无论是否有参),子类的构造方法的第一句编译器将会自动加上super();这个情况前提是父类无有参构造方法,否则编译器不会自动添上super()语句。
    public class Monkey {
        /*如果定义有父类成员变量*/
        public Monkey(){
            /*如果有成员变量,将会赋默认值*/
        }
    }
    class Monkey1 extends Monkey {
        String name;
    
        public Monkey1(String name) {
            super();    //  这段代码如果不人为添上,编译器也会自动添在第一行。
            this.name = name;
            /*子类成员变量的赋值操作,同样赋默认值*/
        }
    }
  3. 当父类定义了有参构造,子类的构造方法需要人为在第一行加上super(参数1,参数2......);当然,在这种情况下,子类也理所应当的为有参构造,否则无法为父类的成员变量初始化
    public class Monkey {
        public int age;
    
        public Monkey(int age){
          this.age=age;
        }
    }
    class Monkey1 extends Monkey {
        public String name;
    
        public Monkey1(int age,String name) {
            super(age);    //  这段代码如果不人为添上,编译器将会报错
            this.name = name;
        }
    }

方法重写 

重写需要满足:(重写是后面实现多态的基础)

  1. 子类定义的方法同父类的重名,且参数列表、返回类型也一样(除非二者的返回类型构成父子类关系),仅仅方法体内容不同。
    public class A{              //public  class A{
        public A func(){}        //    public Object func() {}
    }                            //}
    class B extends A{           //class B extends A{
        public B func(){}        //     public String func() {}
    }                            //}
    //B是A的子类,String类是Object的子类,上面都是合法重写
  2. 如果父类的方法需要重写,父类方法不可为private权限,且子类重写的方法权限必须大于等与被重写方法的权限,public>protected>default(默认访问权限)>private。
  3. 重写方法不可用static、final修饰。 

一般我们在重写的方法上面加上@Override;来标识这个重写方法,能够标识它是否重写,判断是否符合重写规范。

public class Flower {
    void func(){/*代码块...*/}
}
class Rose extends Flower{
    @Override//如果重写不规范将会编译错误。
    void func(){/*代码块...*/}
}

多态 

向上转型和向下转型

向上转型:父类引用子类的实例对象。但是注意下面的flower可以调用func1(),但不可调用func2()(除非func2()是重写方法,这种情况就涉及到多态了),因为flower是父类的引用,不可引用子类的属性和方法(除非func2()是重写的)。

  1. 直接赋值,父类new一个子类对象。
    public class Flower{
        void func1(){}
        void func3(){
            System.out.println("这是父类的...");
        }
    }
    
    class Rose extends Flower{
        void func2(){}
        void func3(){System.out.println("这是子类的...");}
    
        public static void main(String[] args){
            Flower flower=new Rose();
            flower.func1();
            //flower.func2();这句将会报错
            flower.func3();//体现多态,打印的将是:"这是子类的..."
        }
    }

  2. 方法传参。
    public class Flower {
        String color;
        void func(Flower flower){
            System.out.println(flower.color);
    //        System.out.println(flower.age);虽然传过来的是子类引用的
    //子类实例对象,但是在方法传参时发生了向上转型,所以仍然不可以访问子类的age属性。
        }
    }
    class Rose extends Flower{
        int age;
        public static void main(String[] args) {
            Rose flower=new Rose();
            flower.func(flower);
        }
    }

  3. 方法返回值。
    public class Flower{
       
        Flower func(Rose flower){//返回类型为Flower
            return flower;
        }
    }
    class Rose extends Flower{
      
        public static void main(String[] args){
            Rose flower=new Rose();
            Flower flower1=flower.func(flower);//flower1将会引用被向上转型后的flower
        }
    }

向下转型:经过上面的向上转型,我们发现转型后的无法再调用子类特有的方法,那么就可以将向上转型后的再向下转型回来。

public class Flower{
}
class Rose extends Flower{
    void print(){System.out.println("我被向下转型了," +
            "我是Rose的但被赋值给Flower引用了,现在又是Rose了");}
    
    public static void main(String[] args) {
        Flower rose=new Rose();
//        rose.print();编译错误
        ((Rose) rose).print();
    }
}

转来转去,难免绕晕,因此我们引入instanceof来做向下转型前的检查:防止父类类型的对象不是子类对象的实例而出错。

public class Flower{
}
class Rose extends Flower{
    void print(){System.out.println("我是Rose....");}
}
class SunFlower extends Flower{
    void print(){System.out.println("我是SunFlower...");}

    public static void main(String[] args) {
        Rose flower=new Rose();
        Flower flower1=new SunFlower();
        if(flower1 instanceof Rose){//flower1是父类Flower的引用,
            // instanceof判断flower1的对象是不是Rose类
            flower=(Rose)flower1;//强制类型转换
            flower.print();//成功将打印:"我是SunFlower..."
        }else{
            flower.print();//否则打印:"我是Rose...."
        }
    }
}
//最后的结果是打印:"我是Rose....",因为flower1本质是SunFlower而不是Rose

静态绑定、动态绑定

两种绑定在方法调用里的使用效果:

静态绑定:在编译阶段,编译器只知道对象的类型而不知道对象的引用类型,编译器将根据方法传参类型确定调用哪个方法,典型场景:方法重载。

动态绑定:当父类的引用变量指向一个子类对象,这个引用调用重写了的方法时,在编译阶段,由于重写,编译器无法判断调用父类的还是子类的方法,到了Java虚拟机(JVM),JVM将选择调用子类对象里的对象。

public class Flower{
    void print(){System.out.println("我是Flower...");}
}
class Rose extends Flower{
    void print(){System.out.println("我是Rose...");}
}
class SunFlower extends Rose{
    void print(){System.out.println("我是SunFlower...");}
}
class Test{
    public static void main(String[] args) {
        Flower flower1=new Rose();
        Flower flower2=new SunFlower();
        Rose flower3=new SunFlower();
        Flower flower4=new SunFlower();
        flower1.print();//打印:我是Rose...
        flower2.print();//打印:我是SunFlower...
        flower3.print();//打印:我是SunFlower...
        flower4.print();//打印:我是SunFlower...
    }
}

多态正文

多态:不同的对象去完成同一件事会产生不同的结果。多态可以减少代码的“圈复杂度”,避免大量的if-else语句,更直观、好理解,也提高了代码的可扩展度。

注意:

  1. 类中的属性没有多态性,构造方法没有多态性。
  2. 多态性通过继承和方法重写实现。
  3. 必须由父类引用子类对象才可体现多态。

 flower1、2、3三种不同对象,但做func()这同一件事情时,却产生不同效果,这便是多态

public class Flower{
    void print(){System.out.println("我是Flower...");}

}
class Rose extends Flower{
    void print(){System.out.println("我是Rose...");}
}
class SunFlower extends Flower{
    void print(){System.out.println("我是SunFlower...");}
}
class Test{
    public void func(Flower flower){//传过来的引用都将被向上转型,父类
        // 引用子类对象又调用重写方法满足动态绑定的条件
        flower.print();
    }
    public static void main(String[] args) {
        Test test=new Test();
        Flower flower1=new Flower();
        Rose flower2=new Rose();
        SunFlower flower3=new SunFlower();
        test.func(flower1);//打印:我是Flower...
        test.func(flower2);//打印:我是Rose...
        test.func(flower3);//打印:我是SunFlower...
    }
}

完。

  • 18
    点赞
  • 27
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值