Day15:三大特性:封装、继承、多态

 

封装

 

该露的露,该藏的藏

我们程序设计要追求“高内聚,低耦合”。高内聚就是类的内部数据操作细节自己完成,不允许外部干涉;低耦合:仅暴露少量的方法给外部使用

封装(数据的隐藏)

通常,应禁止直接访问一个对象中数据的实际表示,而应通过操作接口来访问,这称为信息隐藏

 

记住这句话就够了:属性私有,get/set

封装大多时候是针对属性,private私有成员变量在本类中可以随意访问,但超出了类的范围就不能访问了。

要想访问私有成员变量,必须定义一对Getter/Setter方法,当然,还可以使用反射且方法必须以getXxx/setXxx规则命名对于getXxx来说,不能有参数,返回值的类型要与成员变量类型一致对于setXxx来说,不能有返回值,参数类型要与成员变量类型一致

对于boolean类型,它的Getter方法必须写成 isXxx 的形式,而 setXxx 规则不变。原文链接:https://blog.csdn.net/qq_34565684/article/details/106334769


//Student
package com.oop.demo04;


//类 private :私有
public class Student {

   //属性私有
   private String name;//名字

   private int id;//学号

   private char sex; //性别
   private int age;//年龄

   //提供一些可以操作这个属性的方法!
   //提供一些public的get、set方法
   //get 获得这个数据
   public String getName(){
       return this.name;
  }

   //set 给这个数据设置值
   public  void setName(String name){
       this.name = name;
  }
   //alt+insert 快捷键

   public int getId() {
       return id;
  }

   public void setId(int id) {
       this.id = id;
  }

   public char getSex() {
       return sex;
  }

   public void setSex(char sex) {
       this.sex = sex;
  }

   public int getAge() {
       return age;
  }

   public void setAge(int age) {
       if(age>120||age<0) {//不合法
           this.age = 3;
      }else{
           this.age = age;
      }
  }
//学习()

   //睡觉()
}

//使用
package com.oop;

import com.oop.demo03.Pet;
import com.oop.demo04.Student;

/*
1.提高程序的安全性,保护数据
2.隐藏代码的实现细节
3.统一接口
4.系统的可维护性增加
*/

public class Application {
   public static void main(String[] args) {
       Student s1= new Student();

       s1.setName("安言");
       System.out.println(s1.getName());
       s1.setAge(10);//不合法的
       System.out.println(s1.getAge());

  }

}

继承

继承的本质是对某一批类的抽象,从而实现对现实世界更好的建模

extends的意思是"扩展"。子类是父类的扩展。

Java中类只有单继承,没有多继承(一个儿子只能有一个爹,但是一个爹可以有多个儿子)

继承是类和类之间的一种关系。除此之外,类和类之间的关系还有依赖、组合、聚合等。

继承关系的两个类,一个为子类(派生类),一个为父类(基类)。子类继承父类,使用关键字extends来表示

子类和父类之间,从意义上讲应该具有"is a"的关系

 


public class Student extends Person //继承

Person person;//Student类里面包含了Person

在Java中,所有类,都默认直接或者间接继承Object
  //public
  //private
  //default 默认的
  //protected 受保护的
  //ctrl+h 快捷键 查看父子关系

//使用
//Person 人:父类
public class Person /*extends Object*/ {

   //public
   //private
   //default 默认的
   //protected 受保护的
 private int money  = 10_0000_0000;
   public void say(){
       System.out.println("说话");
  }

   public int getMoney() {
       return money;
  }

   public void setMoney(int money) {
       this.money = money;
  }

}
//学生 is 人 :子类
public class Student extends Person{
   //ctrl+h

}


public static void main(String[] args) {
       Student student = new Student();
       student.say();
       student.setMoney(1_0000_0000);
       System.out.println(student.getMoney());

  }

Super

和this对比看

this访问本类的属性

super是子类通过super访问父类的属性


public Student() {
       System.out.println("Student无参执行了");
  }

 public Person() {
       System.out.println("Person无参执行了");
  }

public static void main(String[] args) {
       Student student = new Student();
       /*student.test("安言");
       student.test1();*/

  }

输出结果是:

"Person无参执行了

Student无参执行了

因为子类中隐藏代码:调用了父类的无参构造


super注意点:
  1.super调用父类的构造方法,必须在构造方法的第一个
    public Student() {

      super();//调用父类的构造器,必须要在子类构造器的第一行
      System.out.println("Student无参执行了");
  }ok!!!
    public Student() {
      System.out.println("Student无参执行了");
        super();
  }//报错
   
  2.super必须只能出现在子类的方法或者构造方法中!
  3.super和this不能同时调用构造方法
Vs this
  代表的对象不同:
    this:本身调用者这个对象
    super:代表父类对象的应用
  前提:
      this:没有继承也可以使用
      super:只能在继承条件下才可以使用
  构造方法
      this();本类的构造
      super();父类的构造

父类


//Person 人:父类
public class Person /*extends Object*/ {


   public Person() {
       System.out.println("Person无参执行了");
  }
  public Person(String name) {
      System.out.println("Person无参执行了");
  }
   //public
   //private
   //default 默认的
   //protected 受保护的
     protected String name = "anyan";
  //私有的东西无法被继承
   private void print1(){
       System.out.println("Person");
  }

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

子类


package com.oop.demo05;

//学生 is 人 :子类
public class Student extends Person{
   //ctrl+h

  //隐藏代码:调用了父类的无参构造

   public Student() {

       super();//调用父类的构造器,必须要在子类构造器的第一行
       System.out.println("Student无参执行了");
  }

   private  String name = "tianyu";

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

   public  void test1(){
     print();
     this.print();
     super.print();

  }

 public  void test(String name){
     System.out.println(name);
     System.out.println(this.name);
     System.out.println(super.name);
}
}

如果父类里面只有有参构造方法



  public Person(String name) {
      System.out.println("Person无参执行了");
  }

在子类里面


 public Student() {

       super();//调用父类的构造器,必须要在子类构造器的第一行
       System.out.println("Student无参执行了");
  }

就会报错,因为一旦定义了有参构造方法,那么无参构造方法就没有了,必须显示出来,即使什么也不写


    public Person() {
     
  }

public Person(String name) {
      System.out.println("Person无参执行了");
  }

方法重写

静态


public class A extends  B {
   public static void test(){
       System.out.println("A=>test()");

  }
}


//重写都是方法的重写,和属性无关
public class B {
   public static void test(){
       System.out.println("B=>test()");

  }
}


public class Application {
   public static void main(String[] args) {
//方法的调用只和定义的数据类型有关
A a = new A();
a.test();//A

//父类的引用指向了父类
B b = new A();
b.test();//B

  }

}

非静态



//重写都是方法的重写,和属性无关
public class B {
   public  void test(){
       System.out.println("B=>test()");

  }
}


   //重写
   @Override//注解:有功能的注释
   public void test() {
       System.out.println("A=>test()");
  }
}

public class Application {

   //静态方法和非静态方法区别很大
 
   public static void main(String[] args) {
//静态方法的调用只和定义的数据类型有关
//非静态方法:重写
A a = new A();
a.test();//A

//父类的引用指向了父类
B b = new A();//子类重写了父类的方法
b.test();//A

  }

}

 

因为静态方法是类的方法,而非静态方法是对象的方法。

有static时,b调用了B类的方法,因为b是用b类定义的

没有static时,b调用的是对象的方法,而b是用A类new的

重写:需要有继承关系,子类重写父类的方法!


1.方法名必须相同
2.参数列表必须相同
3.修饰符:范围可以扩大: public>protected>default>private
子类不能重写父类声明为private权限的方法。
4.抛出的异常,范围,可以被缩小,但不能扩大;例如:父类的一个方法申明了一个检查异常IOException,在重写这个方法是就不能抛出Exception,只能抛出IOException的子类异常,可以抛出非检查异常。
重写,子类的方法和父类的方法必须要一致;方法体不同!
5.返回值类型
父类被重写的方法的返回值是void,则子类重写的方法的返回值类型只能是void
父类被重写的方法的返回值类型是A类型,则子类重写的方法的返回值类型可以是A类或A类的子类
父类被重写的方法的返回值类型是基本数据类型(比如double),则子类重写的方法的返回值必须是相同的基本数据类型(必须是double)

为什么需要重写:
1.父类的功能,子类不一定需要,或者不一定满足!
Alt + Insert:override
//考察多态的笔试题
public class InterviewTest{
 public void main (String[] args){
   //编译看左,运行看右
   Base1 base = new Sub1();
   base.add(1,2,3);
   Sub1 s = (Sub1)base;
   s.add(123);
}
 
 
 class Base1{
   //第一步
   public void add(int a,int...arr){
     System.out.println("base1");
  }
}
 class Sub1 extends Base1{
   //第二步
   public void add(int a,int[] arr){
     System.out.println("sub_1");
  }//是重写
    public void add(int a,int b,int c){
     System.out.println("sub_2");
  }
}
}
//运行结果:
//sub_1
//sub_2

 

多态

对象的多态性:父类的引用指向子类的对象(或子类的对象赋给父类的引用)

多态的变量,想调用子类的方法,必须在父类有,并且在子类重写父类的方法

动态编译:类型只有在执行过程中才能确定,可扩展性更强

即同一方法可以根据发送对象的不同而采用多种不同的行为方式

一个对象的实际类型是确定的,但可以指向对象的引用的类型有很多(父类,有关系的类)

 

编译看左,运行看右

有了对象的多态性后,我们在编译器,只能调用父类中声明的方法,但在运行期,我们实际执行的是子类重写父类的方法。

 

多态存在的条件

有继承关系

子类重写父类方法

父类引用指向子类对象

 

多态的使用:当调用子父类同名同参数的方法时,实际执行的是子类重写父类的方法---虚拟方法调用

注意:多态是方法的多态,属性没有多态性

instanceof 类型转换---引用类型


package com.oop.demo06;

public class Person {
   public void run(){
       System.out.println("run");
  }
}

package com.oop.demo06;

public class Student extends Person{
   public void run(){
       System.out.println("son");
  }
   public void eat(){
       System.out.println("eat");
  }

}
/*
多态注意事项:
1.多态是方法的多态,属性没有多态
2.父类和之类,有联系,     类型转换异常,必须存在继承关系   ClassCastException!
3.存在条件:继承关系,方法需要重写,父类引用指向子类对象     Father f1 = new Son();

1.static方法 属于类,它不属于实例
2.final 是常量,不能
3.private方法:私有的,不能
*/

package com.oop;

import com.oop.demo05.A;
import com.oop.demo05.B;

import com.oop.demo06.Student;
import com.oop.demo06.Person;


/*
1.提高程序的安全性,保护数据
2.隐藏代码的实现细节
3.统一接口
4.系统的可维护性增加
*/

public class Application {


   public static void main(String[] args) {
       //一个对象的实际类型是确定的
       // new Student();
       //new Person();

       //可以指向的引用类型就不确定了
       //Student 能调用的方法都是自己的或者继承父类的
       Student s1= new Student();
       //Person 父类型,可以指向子类,但是不能调用子类独有的方法
       Person s2 =  new Student();//父类的引用指向子类
       Object s3 = new Student();

       //对象能执行哪些方法,主要看对象左边的类型,和右边关系不大
       s2.run();//子类重写了父类的方法,执行子类的方法
       s1.run();
       //s2.eat();不行
      ((Student) s2).eat();
  }

}

重写是运行时多态,重载是编译时多态

 

虚拟方法调用(多态情况下)

子类中定义了与父类同名同参数的方法,在多态情况下,将此时父类的方法称为虚拟方法,父类根据赋给它的不同子类对象,动态调用属于子类的该方法。这样的方法调用在编译期是无法确定的。

多态是运行时行为


class Animal{
 protected void eat(){
   System.out.println("animal eat food");
}
}
class Cat extends Animal{
 protected void eat(){
   System.out.println("cat eat fish");
}
}
class Cat extends Animal{
 protected void eat(){
   System.out.println("cat eat fish");
}
}
class Dog extends Animal{
 public void eat(){
   System.out.println("dog eat bone");
}
 
}
class Sheep extends Animal{
 public void eat(){
   System.out.println("dog eat bone");
}
 
}
public static Animal getInstance(int key){
 switch(key){
   case 0:
     return new Cat();
   case 1:
     return new Dog();
   case 2:
     return new Sheep();
}
}
public static void main(String[] args){
 int key = new Random.nextInt(3);
 
 System.out.println(key);
 
 Animal animal = getInstance(key);
 
 animal.eat();
}

方法的重载与重写

从编译和运行的角度看:

重载,是指允许存在多个同名方法,而这些方法的参数不同。编译器根据方法不同的参数表,对同名方法的名称做修饰。对于编译器而言,这些同名方法就成了不同的方法。它们的调用地址在编译期就绑定了。Java的重载是可以包括父类和子类的,即子类可以重载父类的同名不同参数的方法。

所以:对于重载而言,在方法调用之前,编译器就已经确定了所要调用的方法,这称为”早绑定“或”静态绑定“

 

而对于多态,只有等到方法调用的那一刻,解释运行器才会确定要调用的具体方法,这称为”晚绑定“或”'动态绑定'。

引用一句Bruce Ecke的话:“不要犯傻,如果它不是晚绑定,他就不是多态”。


//==,对于引用数据类型来讲,比较的是两个引用数据类型变量的地址值是否相等
System.out.println(b==s)
 //Sub s = new Sub()
 //Base b =s
 //Base是父类,Sub是子类
 

 

instanceof

当进行向下转型之前进行判断。

可以判断两个类之间是否存在父子关系。(类型转换)引用类型,判断一个对象是什么类型

instanceof 左边是对象,右边是类;当对象是右边类或子类所创建的对象时,返回true,否则,返回false

说明:

类的实例包含本身的实例,以及所有直接或间接子类的实例

instanceof左边显式声明的类型与右边操作元必须是同种类或存在继承关系,也就是说需要位于一个继承树,否则会编译错误。


System.out.println(x  instanceof y);//能不能编译通过

要看编译能不能通过,就看x和y有没有父子关系


public class Application {


   public static void main(String[] args) {
       //Object > Person >Student
       //Object > Person >Teacher
       //Object > String
       Object object = new Student();

       System.out.println(object instanceof Student);//true
       System.out.println(object instanceof Person);//true
       System.out.println(object instanceof Object);//true
       System.out.println(object instanceof Teacher);//false
       System.out.println(object instanceof String);//false
       System.out.println("=========================");
       Person person = new Student();
       System.out.println(person instanceof Student);//true
       System.out.println(person instanceof Person);//true
       System.out.println(person instanceof Object);//true
       System.out.println(person instanceof Teacher);//false
       System.out.println(person instanceof String);//编译报错
       System.out.println("=========================");
       Student studnet = new Student();
       System.out.println(studnet instanceof Student);//true
       System.out.println(studnet instanceof Person);//true
       System.out.println(studnet instanceof Object);//true
       System.out.println(studnet instanceof Teacher);//编译报错
       System.out.println(studnet instanceof String);//编译报错

  }

}

几个注意点:

创建子类时,会加载所有的父类,转型只能转为已经加载了的类


问题一:编译时通过,运行时不通过
举例一:
Person p3 = new Woman();
Man m3 = (Man)p3;
举例二:
Person p4 = new Person();
Man m4 = (Man)p4;

问题二:编译通过,运行也通过
Object obj = new Woman();
Person p = (Person)obj;

问题三:编译不通过
Man m5 = new Woman();

向上转型:多态

向下转型:使用instanceof进行判断

posted @ 2022-01-17 21:27  安言mty  阅读(85)  评论(0编辑  收藏  举报