Java 基础(对象)五

342 阅读20分钟

对象 Object

OOP特征二: 继承性(inheritance)

class className1 extends ClassName2 {}: className1 继承了 className2 的属性和方法

继承性的好处:

  • 减少代码冗余,提高了代码的复用性

  • 便于功能的扩展

  • 为之后多态性的使用,提供了前提

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

  • A: 子类、subclass

  • B: 父类、superclass

  • 子类继承父类后,可以声明自己的属性和方法

体现: 一旦子类A继承父类B后,子类A就获取了父类B中的结构: (所有的)属性和方法

特别的: 父类中声明为 private 的属性或方法,子类继承父类后,仍然认为获取到了父类中的私有属性或方法,只是因为封装性的影响,使得子类不能直接调用父类的结构而已

public class Person {
	String name;
    private int age;
   	
    public Person () {}
    
    public int getAge () {
    	return age;
    }
    public void setAge (int age) {
    	this.age = age;
    }
    public void eat () {
    	System.out.println("eat");
    }
    public void sleep () {
    	System.out.println("sleep");
    }
}
public class Student extends Person {
	// String name;
    // int age;
    String major;
    
    public Student () {}
    public Student (String name, int age, String major) {
    	this.name = name;
        this.age = age;
        this.major = major;
    }
    
    // public void eat () {
    // 	System.out.println("eat");
    // }
    // public void sleep () {
    // 	System.out.println("sleep");
    // }
    public void study () {
    	System.out.println("study");
    }
    // Student 中不能直接使用 age 属性
    public void show () {
    	System.out.println("name: " + name + "age: " + getAge());
    }
}
public class extendsTest {
	public static void main(String[] args) {
    	Person p1 = new Person();
        p1.age = 1;
        p1.eat(); // eat
        
        Student s1 = new Student();
        s1.name = "Jerry";
        s1.eat(); // 
        s1.sleep();
        s1.study();
        s1.setAge(10);
        s1.getAge(); // 10
    }
}

Java中关于继承性的规定

  • Java 只支持单继承和多层继承,不允许多重继承

    • 单继承性, 一个子类只能有一个父类
    • 一个父类可以派生多个子类
    • 多层继承,子父类是相对概念
  • 子类继承父类后,就获取了直接父类以及所有间接父类中声明的属性和方法

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

  • 所有的 java类,都直接或间接继承于 java.lang.Object 类

总结:

继承性

继承性的优点

  • 减少代码冗余,提高代码复用性

  • 便于功能扩展

  • 为之后多态性的使用,提供了前提

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

子类继承父类后的特点

  • 一旦子类A 继承了父类B 后,子类A 就全部继承了父类B 的属性和方法

  • 子类可以根据需要创建自己的属性和方法

  • 子类中继承父类的属性和方法,仍然受其权限控制符的影响(封装性)

  • 所有的 Java类,都直接或间接继承于 java.lang.Object类(根父类)

方法的重写(override)

定义

在子类中可以根据需要对从父类中继承的方法进行改造,也称为方法的重置、覆盖; 在程序执行时,子类的方法将覆盖父类的方法。

要求

  • 子类重写的方法必须和父类被重写的方法具有相同的 方法名、参数列表

  • 子类重写的方法返回值类型 不大于 父类被重写的方法的返回值类型

    1. 父类 void : 子类 void
    2. 父类 A类 : 子类 A类或A类的子类 (Object : Object或String Array...)
    3. 父类 基本数据类型 : 子类 相对应的基本数据类型 (double : 只能是double)
    4. 子类重写的方法抛出的异常的类型 不大于 父类被重写的 (Exception : Exception或RuntimedException)
  • 子类重写的方法使用的访问权限 不小于 父类被重写的方法的访问权限

  • 特殊情况: 子类不能重写父类中声明为 private 权限的方法

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

注意

子类与父类中同名同参数的方法必须同时声明为非static的(可以考虑重写),或者同时声明为static的(不是重写); 因为static是属于类的,子类无法覆盖父类的方法。

四种访问权限修饰符

protected

在不同包的子类中,不能调用 Order类 中声明为 private 和 缺省 权限的属性、方法

./code/java1

public class Order {
	private int i;
    String str;
    protected int age;
    public String addr;
    
    int getAge () {
    	return age;
    }
    protected String getStr () {
    	return str;
    }
}
./code/java2

import code.java1.Order;
public class SubOrder extends Order {

	public void method () {
    	i = 10;
        age = 12;
        
        // getAge(); error 权限不够
        getStr();
    }
}

关键字: super

主要解决 子父类属性、方法冲突 的问题

super 关键字的使用

  • super 理解为: 父类的...

  • super 可以用来调用: 属性、方法、构造器

  1. 我们在子类的方法和构造器中,通过使用 super.属性super.方法的方式,显示的调用父类中声明的属性和方法;但是一般可以省略

  2. 属性-特殊情况: 当父类和子类中定义了同名的属性时,如果要在子类中使用父类中的属性和方法,必须显示的使用 super.属性super.方法 表明使用的是父类中的属性和方法

  3. 方法-特殊情况: 当子类重写了父类的方法后,可以使用 super.方法 指定调用父类中被重写的方法

  4. 构造器: 我们可以在子类的构造器中显示的使用 super(参数列表) 的方式,调用父类中声明的指定的构造器;

    • super(参数列表)的使用,必须声明在子类构造器的首行,所以 this(参数列表)super(参数列表) 在类的构造器中只能 二选一

    • 在子类的构造器中,没有显示使用 this(参数列表)super(参数列表)的情况下,默认使用 super() 调用父类中的 空参构造器

    • 在类的多个构造器中,至少有一个类的构造器中使用了 super() 调用父类中的构造器

public class Person {
	String name;
    int age;
    String id = 10001; // 身份证
    
    public Person () {}
    public Person (String name) {
    	this.name = name;
    }
    public Person (Sring name, int age) {
    	this(name);
        this.age = age;
    }
	
    public void eat () {
    	System.out.println("eat");
    }
    public void walk () {
    	System.out.println("walk");
    }
}
public class Student extends Person {
	String major;
    String id = 1002; // 学号
    
    public Student () {}
    public Student (String major) {
    	this.major = major;
    }
    public Student (String name, int age, String major) {
    	super(name, age); // 调用父类构造器
        this.major = major;
    }
    public void eat () {
    	System.out.println("student eat much !");
    }
    public void show () {
    	System.out.println("name: " + name + ", age: " + age);
        System.out.println("this.id: " + id); // 子类 学号
        System.out.println("super.id" + super.id); // 父类 身份证
        this.eat(); // 子类eat
        super.eat(); // 父类eat
    }
}

public class SuperTest {
	public static void main(String[] args) {
    	Student s1 = new Student();
        s1.show();
        
        Student s2 = new Student('lv', 22, 'computer');
    }
}

子类对象实例化过程

从结果上来看

  • 子类继承了父类以后,就获取了父类中声明的属性或方法

  • 创建子类的对象,在堆空间中,就会加载所有父类中声明的属性和方法

从过程上来看

当我们通过子类的构造器创建子类对象时,我们一定会直接或间接调用其父类的构造器,进而调用父类的父类的构造器,...,直到调用了 java.lang.Object类中空参构造器为止。

正因为子类加载过所有父类的构造器,所以,我们才能将父类中的属性和方法加载到内存中,从而子类的对象才可以考虑进行调用

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

OOP特征三: 多态性

多态性(适用于方法)

多态性,是面向对象中最重要的概念,只适用于方法不适用于属性,在Java中的体现:

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

    • 可以直接应用在抽象类和接口上
    • 格式: 父类 变量 = new 子类();

Java引用变量有两个类型: 编译时类型和运行时类型;编译时类型由声明时该变量时使用的类型决定,运行时类型由实际赋给该变量的对象决定;简称: 编译时,看左边、运行时,看右边。

  • 若编译时类型和运行时类型不一致,就出现了对象的多态性

  • 多态情况下(只适用于方法):

    • 看左边: 看的是父类的引用(父类中不具备子类特有的方法)
    • 看右边: 看的是子类的对象(实际运行的是子类重写父类的方法)
  • 多态是运行时行为

多态的使用

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

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

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

  • 使用前提:

    • 要有类的继承关系

    • 子类中要有父类方法的重写

public class Person {
	int age;
    String name;
    
    public void eat () {
    	System.out.println("eat");
    }
    public void walk () {
    	System.out.println("walk");
    }
}

public class Man extends Person {
	boolean isSmoke;
    
    public void eat () {
    	System.out.println("Man eat much!");
    }
    public void strong () {
    	System.out.println("Man is very Strong!");
    }
}
puvlic class Woman extends Person {
	boolean isBeauty;
    
    public void eat () {
    	System.out.println("Woman eat less!");
    }
}

public class PersonTest {
	public static void main(String[] args) {
    	Person p1 = new Man();
        p1.eat(); // Man eat much!
        // p1.strong(); 编译报错,Person中没有此方法
        
    }
}
多态性使用实例
public class AnimalTest {
	public static void main(String[] args) {
    	AnimalTest t1 = new AnimalTest();
        t1.fun(new Dog()); // Dog类
        t1.fun(new Cat()); // Cat类
    }
    // public void fun (Dog a1) {
    // public void fun (Cat a1) {
    public void fun (Animal a1) {
    	// Animal a1 = new Dog();
    	a1.eat();
        a1.shout();
    }
}
class Animal {
	public void eat () {
    	System.out.println("eat");
    }
    public void shout () {
    	System.out.println("shout");
    }
}
class Dog extends Animal {
	public void eat () {
    	System.out.println("dog eat!");
    }
    public void shout () {
    	System.out.println("dog shout!");
    }
}
class Cat extends Animal {
	public void eat () {
    	System.out.println("cat eat!");
    }
    public void shout () {
    	System.out.println("cat shout!");
    }
}

// 数据库操作
class Driver {
	public void doData (Connection con) {
    	// con = new MySql()
    	// con = new Oracle()
        // Java规范步骤去操作数据
        con.method1();
        con.method2();
        con.method3();
    }
}
多态性扩展: instanceof 操作符

instanceof的使用: a instanceof A; 判断 对象a 是否是 A的实例,返回值为 true、false

  • 为了在向下强转之前不出现异常,需要在强转之前进行判断

  • 如 a instanceof A 返回true,且 a instanceof B 也返回true,则 B是A的父类

if (p1 instanceof Woman) {
	Woman w1 = (Woman)p1;
    w1.goShopping();
} else if (p1 instanceof Man) {
	Man m1 = (Man)p1;
    m1.earnMoney();
}
向下转型

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

如何解决调用子类特有属性和方法的问题:

  • Person p1 = new Man();

  • 使用向下转型: 强制类型转换符 Man m1 = (Man)p1; 可能会转换失败,比如Woman w1 = (Woman)p1; error: ClassCastException;为避免出错引入关键字: instanceof

  • int -> double (自动类型提升);double -> int (强制类型转换)

  • 子类 -> 父类 (向上转型,多态);父类 -> 子类 (向下转型,instanceof判断)

class Base {
	int count = 10;
    public void display () {
    	System.out.println(this.count);
    }
}
class Sub extends Base {
	int count = 20;
    public void display () {
    	System.out.println(this.count);
    }
}
public class MethodTest {
	public static void main (String[] args) {
    	Sub s = new Sub();
        System.out.println(s.count); // 20
        s.display(); // 20
        
        Base b = s; // 多态性
        // 比较地址值是否相同
        System.out.println(p == s); // true
        System.out.println(b.count); // 10
        b.display(); // 20
    }
}

总结:

  1. 若子类重写父类的方法,就意味着子类里定义的方法彻底覆盖了父类里的同名方法,系统将不可能把父类里的方法转义到子类中;编译看左边,运行看右边

  2. 对于实例的变量则不存在这样的现象,即使子类里定义了与父类完全相同的实例 变量,这个实例的 变量依然不可能覆盖父类中定义的实例变量;编译运行都看左边。

常出现的问题 Error

编译通过,运行不通过

Person p1 = new Woman();

Man m1 = (Man)p1; // error: ClassCastException

Person p2 = new Person();

Man m2 = (Man)p2; // error

编译通过,运行时也通过

Object o1 = new Woman();

Person p1 = (Person)o1;

Person p2 = new Man();

Man m2 = (Man) p2; // 此用法在 Servlet中的 service方法应用

编译不过(不相关的两个类是不可以赋值的)

Man m1 = new Woman(); // error: type of miss match

String s1 = new Date(); // error

Object o1 = new Date();

String s1 = (String)o1; // error

test

public class InterviewTest {
	public static void main(String[] args) {
    	Base b1 = new Sub();
        b1.add(1, 2, 3); // sub_1: 多态性
        
        Sub s1 = (Sub)b1;
        s1.add(1, 2, 3); // sub_2: 优先执行参数确定的方法
    }
}
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");
    }
    public void add (int a, int b, int c) {
    	System.out.println("sub_2");
    }
}

小结

方法的重载与重写

概念

重载: 在同一个类中可以同时声明多个方法名相同但参数列表不同的方法,彼此之间就构成了重载,构造器也可以重载

重写: 子类继承父类后,可以对父类中同名同参数的方法进行覆盖操作

从编译运行的角度看

重载,指类中允许存在多个同名方法,而这些方法的参数不同;编译器根据方法不同的参数表,对同名方法的名称做修饰,对编译器而言,这些同名方法就成了不同的方法;它们的调用地址在编译器就绑定了,这称为早绑定、静态绑定

多态,只能等到方法调用的那一刻,解释器运行器才会确定所要调用的具体方法,这称为晚绑定、动态绑定

引用一句Bruce Ecke: 不要犯傻,如果它不是晚绑定,它就不是多态

多态性、虚拟方法调用
  • 多态性: 父类的引用指向子类的对象

  • 虚拟方法调用: 当我们调用子父类中同名同参的方法时,编译时认为是调用父类中的方法,解释运行时认为调用是子类中的方法

方法重写的具体规则非static问题
  • 方法名、形参列表相同

  • 子类的权限修饰符不能小于父类

  • 返回值类型

  • 子类抛出的异常不能大于父类的异常

  • 开发中建议复制粘贴

super调用构造器具体的注意点
  • this(形参列表): 本类中重载的其它构造器

  • super(形参列表): 调用父类中指定的构造器

    • this和super都只能写在构造器的首行,两者只能二选一

    • 在构造器中默认调用 super() 空参

    • 在一个类的多个构造器中最多出现 n-1个this,最少有一个super

    • 在创建一个子类对象时,一定会调用其父类们的构造器

super或this调用语句只能作为构造器中的第一行出现

子类中,无论哪个构造器创建子类对象,需要先保证初始化父类;目的是: 当子类继承父类后,继承父类中所有的属性和方法,因此子类有必要知道父类如何为对象进行初始化(先明确父类如何做);所以对象中加载的父类的属性顺序: java.lang.Object > 间接父类... > 直接父类 > 本类

Java如果没有多态性那抽象类和接口毫无意义
  • 抽象类和接口不能创建对象,所以只能通过子类继承的方式

Object类的使用

  • Object类是所有Java类的根父类

  • 如果在类的声明中未使用extends关键字指明其父类,默认直接父类是java.lang.Object类

Object类中的功能

  1. equals(): 比较两个对象是否相等

  2. toString(): 返回类名和其引用地址

  3. getClass: 获取当前对象的所属类

  4. hashCode(): 返回当前对象的hash值

  5. clone(): 返回对象的复制体

  6. finalize(): 对象在被销毁之前会调用对象中的finalize方法

  7. wait()

  8. notify()

== 和 equals 的异同

  1. == 既可以比较基本数据类型页可以比较引用数据类型;对于基本数据类型比较的是数据值,对于引用数据类型比较的是内存地址值

  2. equals()的话,它属于java.lang.Object类中的方法,如果该方法没有被重写,默认就是 == ;我们可以看到 String等类的equals方法是被重写过的,而String类在日常开发中用的比较多,就形成了equals是比较值的错误观点

  3. 具体要看自定义类是否重写Object的equals()方法来判断

  4. 通常情况下,重写equals()方法,会比较类中相应的属性是否相等

  5. 基本数据类型使用 ==,引用数据类型使用 equals()

  • ==运算符 的使用:

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

    2. 如果比较的是基本数据类型变量: 比较两个变量保存的数据是否相等(类型可能不同)

    3. 如果比较的是引用数据类型变量: 比较两个对象的地址值是否相等

  • equals()方法的使用:

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

    2. 不能使用在 基本数据类型 变量中,只能适用于引用数据类型

    3. Object类中 equals() 的定义: 和 == 的作用是相同的

      public boolean equals (Object obj) { return (this == obj) }

    4. 像 String、Date、File、包装类等,都重写了Object类中的equals()方法,比较的是两个对象的 ‘实体内容’ 是否相等

    5. 自定义类如何重写 equals() 方法

    int i = 10;
    int j = 10;
    double m = 10.0;
    System.out.println(i == m); // true(自动类型提升)
    
    char c = 10;
    System.out.println(c == i); // true
    
    char c1 = 'A';
    char c2 = 65;
    System.out.println(c1 == c2); // true
    
    // equals
    Customer c1 = new Customer("Tom", 21);
    Customer c2 = new Customer("Tom", 21);
    String s1 = new String("atjava");
    String s1 = new String("atjava");
    
    System.out.println(c1.equals(c2)); // false true(override)
    System.out.println(s1 == s2); // false(地址不同)
    System.out.println(s1.equals(s2)); // true(override)
    
    public class Customer {
    		String name;
         int age;
         
         public boolean equals (Object obj) {
         	if (this == obj) {
             	return true;
             }
             if (obj instanceof Customer) {
             	Customer cust = (Customer)obj;
                 // 比较两个对象的属性是否都相同
                 if (this.name == cust.name && this.name.equals(cust.name)) {
                 	return true;
                 }
             }
             return false;
         }
    }
    

包装类(wrapper)的使用

概念介绍

  • 针对八种基本数据类型定义相应的引用类型 - 包装类(封装类)

  • 有了类的特点,就可以调用类中的方法,Java才是真正的面向对象

    1. byte - Byte
    2. short - Short
    3. int - Integer
    4. long - Long
    5. float - Float
    6. double - Double
    7. boolean - Boolean
    8. char - Character

说明: 以上数值类型的包装类中其直接父类是 Number类

核心: 基本数据类型、包装类、String 三者之间的转化

  • 新特性: JDK 5.0 自动装箱与拆箱 -> 直接赋值
import org.junit.Test;

public class WrapperTest {

	// 1. 基本数据类型 -> 包装类: 调用包装类的构造器
    @Test
    public void test1 () {
    	int i1 = 10;
        Integer i11 = new Integer(i1);
        System.out.println(i11.toString()); // 10
        Integer i22 = new Integer("112");
        System.out.println(i22.toString()); // 123
        
        // Float f11 = new Float(12.33); // double
        // Float f11 = new Float("12.33");
        Float f11 = new Float(12.33f);
        System.out.println(f11.toString());
        
        Boolean b11 = new Boolean(true);
        Boolean b12 = new Boolean("true"); // true
        Boolean b12 = new Boolean("TruE"); // true
        Boolean b13 = new Boolean("true123"); // false
        
        Person p1 = new Person();
        System.out.println(p1.isMale); // false
        System.out.println(p1.isFemale); // null
    }
    // 2. 包装类 -> 基本数据类型: 调用包装类的 xxxValue()
    @Test
    public void test2 {
    	Integer i1 = new Integer(13);
        int i11 = i1.intValue();
        Float f1 = new Float(12.5);
        float f11 = f1.floatValue();
    }
    @Test
    public void test3 () {
    	int n1 = 10;
        // 基本数据类型 -> 包装类
        Integer i1 = new Integer(n1);
        method(i1);
        
        int n2 = 16;
        // JDK 5.0 自动装箱与拆箱 - *****
        // 自动装箱
        Integer i2 = n2;
        boolean b1 = true;
        Boolean b11 = b1; // 自动装箱
        // 自动拆箱
        int n3 = i2;
    }
    public void method (Object obj) {
    	System.out.println(obj.intValue());
    }
    
    // 3. 基本数据类型、包装类 -> String类型: 调用String重载的valueOf(Xxx xxx)
    public void test3 () {
    	int n1 = 10;
        // 连接运算
        String s1 = n1 + "";
        // 调用String的valueOf() - *****
        float f1 = 123.45f;
        String s2 = String.valueOf(f1); // "123.45"
        Double d1 = new Double(45.6);
        String s3 = String.valueOf(d1); // "45.6"
    }
    
    // 4. String类型 -> 基本数据类型、包装类: 调用包装类的parseXxx()
    @Test
    public void test4 () {
    	String s1 = "79";
        // parseXxx() - *******
        int i1 = Integer.parseInt(s1); // 79
        
        String s2 = "true";
        boolean b1 = Boolean.parseBoolean(s2); // true
        String s3 = "true123";
        boolean b1 = Boolean.parseBoolean(s3); // false
    }
}
class Person {
	boolean isMale;
    Boolean isFemale;
}

public class InterviewTest {
	
    @Test
    public void test1 () {
    	// 三元运算符要求类型统一
    	Object o1 = true ? Integer(1) : new Double(2.0); // 变量提升
        System.out.println(o1); // 1.0
    }
    @Test
    public void test2 () {
    	Object o2;
        if (true) {
        	o2 = new Integer(1)
        } else {
        	o2 = new Double(2.0);
        }
        System.out.println(o2); // 1
    }
    @Test
    public void test1 () {
    	Integer i = new Integer(1);
        Integer j = new Integer(1);
        System.out.println(i == j); // false
        // Integer内部定义了IntegerCache结构,IntegerCache中定义了Integer[],
        // 保存了从 -128~127范围的整数。如果我们使用自动装箱的方式,给Integer赋值的范围在
        // -128~127范围内时,可以直接使用数组中的元素,不用再去new了
        // 目的: 提高效率
        Integer m = 1; // 直接在缓存中拿取
        Integer n = 1;
        System.out.println(m == n); // true
        
        Integer x = 123; // 相当于 new Integer(128) 对象
        Integer y = 123;
        System.out.println(x == y); // false
    }
}

包装类实际应用

import java.util.Scanner;
import java.util.Vector;

public class ScoreTest {
	public static void main (String[] args) {
		// instance Scanner
		Scanner scan = new Scanner(System.in);
		// instance Vector obj
		Vector v = new Vector();
		// loop Vector
		int maxScore = 0;
		for (;;) {
			System.out.print("please input student score: ");
			int score = scan.nextInt();
			if (score < 0) {
				System.out.println("Exit !");
				break;
			} else if (score > 100) {
				System.out.println("please again input num !");
				continue;
			}

			// jdk 5.0 before
			// Integer inScore = new Integer(score);
			// v.addElement(inScore); // many atitude
			// jdk 5.0 after
			v.addElement(score); // // auto pack

			if (maxScore < score) {
				maxScore = score;
			}
		}
		// add obj

		// Exit -num

		// get max score

		// loop Vector get A B C ...
		char level;
		for (int i = 0; i < v.size(); i++) {
			Object obj = v.elementAt(i);

			// jdk 5.0 before
			// Integer inScore = (Integer)obj;
			// int score = inScore.intValue();
			// jdk 5.0 after
			// int score = (Integer)obj; // auto unpack
			int score = (int)obj;

			if (maxScore - score <= 10) {
				level = 'A';
			} else if (maxScore - score <= 20) {
				level = 'B';
			} else if (maxScore - score <= 30) {
				level = 'C';
			} else {
				level = 'D';
			}
			System.out.println("student - " + i + " score is " + score + " level is " + level);
		}
	}
}

小结

如何实现向下转型?需要注意的问题?如何解决此问题?

  • 使用强转符: (), Person p1 = new Man(); Man m1 = (Man)p1;

  • 使用instanceof在向下转型前进行判断,

    if (p1 instanceof Man) {
    	Man m1 = (Man)p1;
    }
    

== 和 equals() 的区别

  • == 可以比较基本数据类型和引用数据类型

  • equals() 只能应用在引用数据类型中,比较两个对象是否相等

    1. 如果没有重写equals()方法的话,比较的是对象的地址
    2. 一般重写的eqauls()方法,比较的是存储的内容

重写equals()方法

class User {
	String name;
    int age;
    // 简写版
    public boolean equals(Object obj) {
    	if (obj == this) {
        	return true;
        }
        if (obj instanceof User) {
        	User u1 = (User)obj;
            return this.age == u1.age && this.name.equals(u.name);
        }
        return false;
    }
    // 生成版
    public boolean equals (Object obj) {
    	if (this == obj) {
        	return true;
        }
        if (obj == null) {
        	return false;
        }
        if (getClass() != obj.getClass()) {
        	return false;
        }
        Person p1 = (Person)obj;
        if (age != other.age) {
        	return false;
        }
        if (name == null) {
        	if (other.name != null) {
            	return false;
            }
        } else if (!name.equals(other.name)) {
        	return false;
        }
        return true;
    }
}

写出8种基本数据类型及其对应包装类

  • int : Integer
  • byte : Byte
  • short : Short
  • long : Long
  • char : Character
  • float : Float
  • double : Double

基本数据类型、包装类与String三者如何转换

  • 基本数据类型 -> 包装类

    1. 自动装箱 Integer i1 = 12;
    2. 自动拆箱 int i11 = i1;
  • 基本数据和包装类 -> String

    1. 转为String类型: String.valueOf(Xxx xxx);
    2. 转为包装类: Xxx.parseXxx(String str);

toString()方法

  • 当我们输出一个对象的引用时,实际是在调用当前对象的toString()方法

  • Object类中toString()的定义:

    public String toString () {
    	return getClass().getName() + "@" + Integer.toHexString(hashCode());
    }
    
  • 像String、Date、File、包装类等重写了Object类中的toString()方法,使得在调用对象的toString()时,返回实体内容信息

  • 重写toString()

    public String toString () {
    	return "Customer [name= " + name + " , age= " + age + "]";
    }
    

JUnit单元测试

步骤

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

  2. 创建一个java类,进行单元测试

  3. 要求: 此类是public的、提供一个public无参的构造器

  4. 在此类中声明单元测试方法: 方法权限是public、无返回值、无形参

  5. 此单元测试方法上需要声明注释: @Test,并在单元测试类中导入 org.junit.Test

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

  7. 执行结果无异常: 绿条,反之,红条

实例

import org.junit.Test;

public class JUnitTest {
	
    int num = 10;
    
	@Test
	public void testEquals () { // 非静态
    	String s1 = "MM";
        String s2 = "MM";
        System.out.println(s1.equals(s2));
        // main()方法是静态的必须要造对象
        System.out.println("num: " + num);
        show();
    }
    public void show () {
    	System.out.println("show");
    }
    @Test 
   	public void testToString () {
    	String s3 = "MM";
        System.out.println(s3.toString());
    }
}

复习

向下转型和向上转型

向上转型: 多态

向下转型:

  1. 为何使用向下转型:

有了对象的多态性后,内存中实际上加载了子类特有的属性和方法,但因为声明为父类类型,导致编译时,只能调用父类中声明的属性和方法,无法调用子类特有的属性和方法,使用向下转型可以解决此问题

  1. 如何实现向下转型: ()

  2. 注意点:

    • instanceof判断

多态性的理解

主要目的: 尽可能实现代码的通用性

  1. Object类中定义的 public boolean equals(Object obj){} 方法

  2. JDBC: 使用java程序操作(获取数据库连接、CRUD)数据库(MySql、Oracle...)

  3. 抽象类、接口的使用肯定体现了多态性(没有多态性就没有意义)

多态是运行时行为