第五章:面向对象编程()

2021320

11:41

继承性的格式: class A extends B{}

*              A: 子类、派生类、subclass

*              B: 父类、超类、基类、superclass

*  2.1 体现:一旦子类A继承父类B以后,子类A中就获取了父类B中声明的所有的属性和方法

*      特别地,父类中声明为private的属性或方法,子类继承父类以后,仍然认为获取了父类中私有的结构

*      只是因为封装性的影响,使得子类不能直接调用父类的结构而已

*      private属性,用get、set方法调用

*      private方法,在父类的方法中调用

*  2.2 子类继承父类以后,还可以声明自己特有的属性或方法,实现功能的拓展

 

三、Java中关于继承性的规定:

*  1. 一个类可以被多个子类继承

*  2. Java中类的单继承性:一个类只能有一个父类(直接父类,也就是只能有一个extends)

*  3. 子父类是相对的概念

*  4. 子类直接继承的父类,称为:直接父类。间接继承的父类,称为:间接父类

*  5. 子类继承父类以后,就获取了直接父类以及所有间接父类中声明的属性和方法

 

四、1. 如果我们没有显式地声明一个类的父类的话,则此类继承于java.lang.Object类

*    2. 所有的java类(除java.lang.Object类之外)都直接或间接地继承于java.lang.Object类

*    3. 意味着,所有的java类具有java.lang.Object类声明的功能

 

重写的规定

*         子类重写的方法的方法名和形参列表与父类被重写的方法的方法名和形参列表相同

*         子类重写的方法的权限修饰符不小于父类被重写的方法的权限修饰符

*             > 特殊情况:子类不能重写父类中声明为private权限的方法(不构成重写,两个方法会同时存在。假设eat(),那么子类在继承的父类中的其他方法中调用eat(),是调用的父类中的eat(),子类中调用eat(),是调用的子类中的eat()

*         返回值类型:

*             > 父类被重写的方法的返回值类型是void,则子类重写的方法的返回值类型只能是void,不然会报错

*             > 父类被重写的方法的返回值类型是A类型,则子类重写的方法的返回值类型可以是A类或A类的子类,不然会报错

*             > 父类被重写的方法的返回值类型是基本数据类型(比如:double),则子类重写的方法的返回值类型必须是相同的基本数据类型(必须也是double),不然会报错

*         子类重写的方法抛出的异常类型不大于父类被重写的方法抛出的异常类型

* 一般情况下,项目中重写都是直接复制粘贴父类被重写的方法的结构(除了方法体),或者先写出方法名,然后 alt + / ,IDE会出现重写的选项

*  ****************************************************************

* 子类和父类中的同名同参数的方法要么都声明为非static的(考虑重写),要么都声明为static的(不是重写)

 

super的使用:调用属性和方法

*   3.1 我们可以在子类的方法或构造器中,通过使用"super.属性"或"super.方法"的方式,显式地调用父类中声明的属性或方法。但是,通常情况下,我们习惯省略"super."

*   3.2 特殊情况:当子类和父类中定义了同名的属性时,我们要想在子类中调用父类中声明的属性,则必须显式地使用"super.属性"的方式,表明调用的是父类中声明的属性

*   3.3 特殊情况:当子类重写了父类中的方法以后,我们想在子类的方法中调用父类中被重写的方法时,则必须显式地使用"super.方法"的方式,表明调用的是父类中被重写的方法

 

属性不会像方法一样覆盖(被重写),并且同名属性的类型可以不一样

父类的方法中,默认调用的是父类的属性。哪怕是在子类的方法中又调用父类的方法时

super调用属性和方法的时候,可以向上找多级父类

 

对于多态中同名属性调用的总结:

直接的属性调用(不是在类的方法里调用),看左边,声明的什么样的对象,调用的就是什么样对象的属性

方法里的属性调用:最终在哪个类的方法里直接调用的该属性,调用的就是哪个类的属性

比如Person p = new Man();

p.show()方法实际调用的是Man里重写的方法,那么在方法里调用的时候,调用的就是Man里的属性。如果Man的show()方法调用了p的其他方法dis(),dis里面的属性,用的是p的属性

但是如果是直接的属性调用(不是在类的方法里调用),p.属性就是调用的Person的属性

 

super调用构造器,自己总结的:

* 无论如何,子类初始化都会调用父类相关的构造器。

如果用了"super(形参列表)"显式调用,就是显式调用空参或者有形参的。没有显式调用super的构造器A会默认调用"super()"空参。

如果A显式调用了B构造器"this(形参列表)",调用的B构造器中调用了对应的"super(形参列表)",那么A调用的就是对应的"super(形参列表)",而不再调用空构造器。如果调用的B构造器中没有调用对应的"super(形参列表)",那么B构造器会调用"super()"空参构造器,A也会调用"super()"空参构造器

*

* 如果给父类设置了有参构造器,就一定要给父类设置空参构造器,否则继承的子类构造器没有父类的空参构造器可以调用

 

当我们通过子类的构造器创建子类对象时,我们一定会直接或间接地调用其父类的构造器,进而调用父类的父类的构造器...直到调用了java.lang.Object类中空参的构造器为止。正因为加载过所有的父类的结构,所以才可以看到内存中有父类中的机构,子类对象才可以考虑进行调用

明确:虽然创建子类对象时,调用了父类的构造器,但是自始至终就只创建过一个对象,即为new的子类对象

 

面向对象特征之三:多态性

*

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

* Person p = new Man();

*

* 多态的使用:虚拟方法调用

* 有了对象的多态性以后,我们在编译期,只能调用父类中声明的方法(点击方法会跳转到父类对应的方法中),但在运行期,我们实际执行的是子类重写父类的方法

*   总结:编译看左边,运行看右边

*

* 多态性的使用前提       类的继承关系 方法的重写

 

对象的多态性,只适用于方法,不适用于属性(编译和运行都看左边)!

 

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

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

Person e = new Student();

e.getInfo();

//调用Student类的getInfo()方法

l 编译时类型和运行时类型

编译时ePerson类型,而方法的调用是在运行时确定的,所以调用的是Student类的getInfo()方法。——动态绑定

 

多态性是运行时类型

 

有了对象的多态性以后,内存中实际上是加载了子类特有的属性和方法的,但是由于变量声明为父类类型,导致编译时,只能调用父类中声明的属性和方法。子类特有的属性和方法不能调用

那么如何才能调用子类特有的属性和方法?

我们可以向下转型:使用强制类型转换符

Person person = new Man();

Man m = (Man)person;

 

子类转父类  直接new一个子类赋值给父类对象(多态)

父类转子类  向下转型(前提对象本来就是一个多态化的子类对象。如果是父类对象的对象,不能强转为子类对象)

Person p = new Person();

Man m = (Man)p; // 编译失败 java.lang.ClassCastException

 

Object obj = new Woman();

Person p = (Person)obj;        // 这样写没问题,obj本身属于一个Woman对象,可以强转为Person

 

           * instanceof关键字的使用

           *

           * a instanceof A  判断对象a是否是类A的实例。如果是,返回true;如果不是,返回false

           *

           * 使用情境:为了避免在向下转型时出现ClassCastException的异常,我们在向下转型之前,先进行instanceof的判断,一旦返回true,就进行向下转型。如果返回false,不进行向下转型

           * 如果a instanceof A返回true,则a instanceof  B也返回true。其中,类B是类A的父类

           *

           * Man m = new Man();那么 m instanceof Man,并且m  instanceof Person

           * Person m = new Man();同样 m instanceof Man,并且m instanceof Person

a所属的类与类A必须是子类和父类的关系,否则编译错误

 

 

int... arr和int[] arr

可变形参和数组看作是相同的形参,算重写

class Base {

     public void add(int a, int... arr) {

          System.out.println("base");

     }

}

class Sub extends Base {

     public void add(int a, int[] arr) { // 看作重写

          System.out.println("sub_1");

     }

}

 

Object类的使用

Object类没有属性,只声明了一个空参的构造器

 

finalize() Object类对象会在被回收前被垃圾处理器调用finalize()方法

程序员可以通过System.gc()或者Runtime.getRuntime().gc()来通知系统进行垃圾回收,会有一些效果,但是系统是否进行垃圾回收依然不确定。

垃圾回收机制回收任何对象之前,总会先调用它的finalize方法(如果覆盖该方法,让一

个新的引用变量重新引用该对象,则会重新激活对象)。

永远不要主动调用某个对象的finalize方法,应该交给垃圾回收机制调用。

 

getClass() 获取对象所属的类

Person p = new Student();

System.out.println(p.getClass()); // 会返回Student

 

int i = 10;

char c = 10;

i == c;  // true;

 

* 面试题: == 和 equals() 的区别

* ==:运算符

* 1. 可以使用在基本数据类型变量和引用数据类型变量中

* 2.     如果比较的是基本数据类型变量,比较两个变量保存的数据是否相等。(不一定类型要相同)

*        如果比较的是引用数据类型变量,比较两个对象的地址值是否相同。即两个引用是否指向同一个对象实体

补充: == 符号使用时,必须保证符号左右两边的变量类型一致

System.out.println("hello" == new java.util.Date()); // 编译不通过

* equals()方法的使用

* 1. 是一个方法,而非运算符

* 2. 只能适用于引用数据类型

* 3. Object类中equals()的定义:

*        public boolean equals(Object obj){

*             return (this == obj);

*        }

*        说明:Object类中定义的equals()和==的作用是相同的:比较两个对象的地址值是否相同,即两个引用是否指向同一个对象实体

* 4. 像String、Date、File、包装类等都重写了Object类中的equals()方法。重写以后,比较的不是两个引用的地址是否相同,而是比较两个对象的"实体内容"是否相同

*

* 5. 通常情况下,我们自定义的类如果使用equals()的话,也通常是比较两个对象的"实体内容"是否相同。那么我们就需要对Object类中的equals()进行重写

*        重写的原则:比较两个对象的实体内容是否相同

*        注意:重写的方法中,比较两个对象的字符串属性的时候,要用equals(),不要用==判断。

 

          String s1 = "abcd";

          String s2 = "abcd";

          System.out.println("s1: " + s1 + ",s2: " + s2 +  ",s1 == s2为" + (s1 == s2));// true

          

          String s3 = new String("cdef");

          String s4 = "cdef";

          System.out.println("s3: " + s3 + ",s4: " + s4 +  ",s3 == s4为" + (s3 == s4));// false

          

          String s5 = new String("hijk");

          String s6 = new String("hijk");

          System.out.println("s5: " + s5 + ",s6: " + s6 +  ",s5 == s6为" + (s5 == s6));// false

 

* Object类中toString()的使用:

*

* 1. 当我们输出一个对象的引用时,实际上就是调用当前对象的toString()

Customer c1 = new Customer();

System.out.println(c1); // com.atguigu.java1.Customer@26f0a63f

System.out.println(c1.toString()); // com.atguigu.java1.Customer@26f0a63f

* 2. Object类中toString()的定义:

*         public String toString() {

                return getClass().getName() + "@" +  Integer.toHexString(hashCode());

*        }

* 3. 像String、Date、File、包装类等都重写了Object类中的toString()方法,使得在调用对象的toString()时,返回"实体内容"信息

* 4. 自定义类也可以重写toString()方法,当调用此方法时,返回对象的"实体内容"

 

* Java中的JUnit单元测试

*

* 步骤:

* 1. 选中当前工程 - 右键选择: build path - add libraries  - JUnit 4 - 下一步

* 2. 创建Java类,进行单元测试。

*        此时的Java类要求: 此类是public 此类提供公共的无参的构造器

* 3. 此类中声明单元测试方法。(通常取名为 testXxx(){})

*        此时的单元测试方法:方法的权限是public,没有返回值,没有形参

* 4. 此单元测试方法上需要声明注解:@Test,并在单元测试类中导入:import org.junit.Test;

*

* 5. 声明好单元测试方法以后,就可以在方法体内测试相关的代码。

* 6. 写完代码以后,左键双击单元测试方法名,右键:run as -  JUnit Test

*

* 说明:

* 1. 如果执行结果没有任何异常:绿条

* 2. 如果执行结果出现异常:红条

*

* 单元测试中,遇到异常也会停止,执行不了下面的代码

* 单元测试中可以引用属性和其他方法

 

包装类(Wrapper)

 

 

 

基本数据类型 ---> 包装类:调用包装类的构造器

        int num1 = 10;

        Integer in1 = new Integer(num1);

无法把字符串转换成Integer时,会报异常

Boolean进行了优化,如果传入的不是"true(不分大小写)",就全部是false,不会报错

Integer in3 = new Integer("1234abc"); //异常

Boolean b3 = new Boolean("true123"); //false

包装类 ---> 基本数据类型:调用包装类Xxx的xxxValue()方法

Integer in1 = new Integer(15);

int i1 = in1.intValue();

JDK 5.0新特性:自动装箱与自动拆箱

int num2 = 10;

Integer in1 = num2; //自动装箱

int num3 = in1;//自动拆箱

基本数据类型、包装类 ---> String类型:调用String重载的valueOf(Xxx xxx)

int num1 = 10;

//方式1:连接运算

String str1 = num1 + "";

//方式2:调用String的valueOf(Xxx xxx)

float f1 = 12.3f;

String str2 = String.valueOf(f1);

System.out.println(str2);

Double d1 = new Double(12.4);

String str3 = String.valueOf(d1);

System.out.println(str3);

String类型 ---> 基本数据类型、包装类:调用包装类的parseXxx(String s)

String str1 = "123";

//错误的情况

//        int num1 = (int)str1;

//        Integer in1 = (Integer)str1; 对象没有子父类关系无法强转

          

//可能会报NumberFormatException,比如str1 = "123abc"

          int num2 = Integer.parseInt(str1);

          System.out.println(num2 + 1); // 124

          

//Boolean进行了优化,如果传入的不是"true(不分大小写)",就全部是false,不会报错

          String str2 = "true";

          boolean b1 = Boolean.parseBoolean(str2); //true

          boolean b2 = Boolean.parseBoolean(str1); //false

          System.out.println(b1);

          System.out.println(b2);

附:

class Person{

     boolean isMale;

     Boolean isM;

     Boolean isFemale = new Boolean(true);

}

Person p = new Person();

System.out.println(p.isMale); // false

System.out.println(p.isM); // null null就返回null

System.out.println(p.isFemale); // true 不是null就调用对象的toString()方法,而不是地址值!

System.out.println(Object x); null就返回null,不是null就调用对象的toString()方法,Boolean类重写了toString(),所以会返回true而不是地址值

 

Object o1 = true ? new Integer(1) : new Double(2.0);

System.out.println(o1);// 1.0

三元运算符要求两个结果类型相同,所以int的1会自动类型提升为double型1.0

Object o1 = true ? 1.0 : 2.0;

然后 Object o1 = 1.0;

自动装箱 Object o1 = new Double(1.0);

 

Object o = 1; // 自动装箱

System.out.println(o.getClass()); // class java.lang.Integer

 

Integer内部定义了IntegerCache结构,IntegerCache中定义了Integer[],保存了从-128~127范围的整数。如果我们使用自动装箱的方式,给Integer赋值的范围在-128~127范围内时,可以直接使用数组中的元素,不用再去new了。目的:提高效率

          Integer m = 1;

          Integer n = 1;

          System.out.println(m == n);// true

          Integer x = 128; // 相当于new了一个Integer对象

          Integer y = 128; // 相当于new了一个Integer对象

          System.out.println(x == y);// false

 

谈谈你对多态性的理解?

 实现代码的通用性

 举例

Object类中定义的public boolean equals(Object obj){}

JDBC: 使用java程序操作(获取数据库连接、CRUD)数据库(MySQL、Oracle、DB2、SQL Server)

 抽象类、接口的使用肯定体现了多态性(抽象类、接口不能实例化)

 

数组也可以看做是Object的一个子类,可以调用Object的一些方法

int[] arr = new int[]{1,2,3};

syso(arr.getClass().getSuperclass()); // Object

 

String s = "abc";

s = null;

System.out.println(s); // null

System.out.println(s.toString()); // 空指针异常

System.out.println(对象名)大部分是直接调用对象的toString()方法,但是如果对象是null的话,会有一个保护机制,直接输出null,不去调用toString()方法了

 

String s2 = "1728471.223f";

int i2 = Integer.parseInt(s2); // 报错  NumberFormatException

          

结尾

 

使用 Microsoft OneNote 2016 创建。 版权所有:古木苏