5,对象的多态性
在Java中主要有两种体现形式:(1)方法的重载和重写。(2)对象的多态性。
5.1,方法的重载(覆盖)和重写(覆写)
5.2,对象的多态性
(1)向上转型:子类对象➡父类对象,程序会自动完成,父类 父类对象 = 子类实例。
class A{ public void fun(){ System.out.println("Hello Word!"); } } class B extends A{ public void fun(){ System.out.println("Hello Java!"); } public void funB(){ } } public class HelloWord{ public static void main(String[] args) { B b = new B(); A a = b; a.fun(); } } ============================================= Hello Java!
虽然使用父类对象调用了fun()方法,但实际上调用的方法是被子类覆写过的方法。也就是说,如果对象发生了向上转型关系后,所调用的方法一定是被子类覆写过的方法。
注意:此时对象a无法调用B类中的funB()方法的,因为此方法只在子类定义而没有在父类中定义,如果要想调用子类自己定义的其他方法,则肯定要使用子类实例,所以此时可以将对象进行向下转型。
(2)向下转型:父类对象➡子类对象,明确地指明要转型的子类类型,子类 子类对象 = (子类)父类实例。
class A{ public void fun(){ System.out.println("Hello Word!"); } public void funA(){ this.fun(); } } class B extends A{ public void fun(){ System.out.println("Hello Java!"); } public void funB(){ System.out.println("Hello China"); } } public class HelloWord{ public static void main(String[] args) { A a = new B(); B b = (B)a; b.fun(); b.funB(); b.funA(); } } ============================================ Hello Java! Hello China Hello Java!
如果想调用子类自己的方法,则一定只能用子类的实例化对象。另外,在子类对象里调用了从父类中继承而来的funA方法,funA()方法调用fun()方法,由于此时fun()方法已经被子类所覆写,所以,此时调用的方法是被子类覆写过的方法。
注意:在对象的向下转型前必须首先发生对象向上转型,否则将会出现对象转型异常。
A a = new A(); B b = (B)a; b.fun(); b.funA(); b.funB(); ========================== Exception in thread "main" java.lang.ClassCastException: Package2.A cannot be cast to Package2.B at Package2.HelloWord.main(HelloWord.java:21)
语法上并没有错误,但是运行时会发生异常。原因:假如你今天刚买完一些东西,回家的路上碰见一个孩子,这个孩子忽然对你说:“我是你的孩子,您把你买的东西交给我吧!”,这时你肯定不会交给他,因为你不确定是否跟这个孩子有关系。父类用其本身类实例化自己的对象,但是并不知道自己的子类,那肯定在转换时会出现错误,那么这个错误该如何纠正,只需要将两个对象建立好关系即可。在声明父类对象时先发生向上转型关系“A a = new B();”,这是相当于时由子类去实例化父类对象,也就是说这时父类知道有这么一个子类,也就相当于父亲知道了自己有这么一个孩子,所以上面的转型就不会有错误了。
5.3,多态性的实际功能
不使用对象多态性实现功能:虽然实现了基本的要求,但是如果按照下面的方式完成程序,则当产生一个A类的休息时,fun方法就产生一次重载,则每一次扩充子类都必须修改类本身,这样可定不方便。
class A{ public void fun1(){ System.out.println("A-fun1()"); } public void fun2(){ fun1(); } } class B extends A{ public void fun1(){ System.out.println("B-fun1()"); } public void fun3(){ System.out.println("B-fun3()"); } } class C extends A{ public void fun1() { System.out.println("C-fun1()"); } public void fun4(){ System.out.println("C-fun4"); } } public class HelloWord{ public static void main(String[] args) { fun(new B()); fun(new C()); } public static void fun(B b){ b.fun1(); } public static void fun(C c){ c.fun1(); } } ================================ B-fun1() C-fun1()
使用对象多态性实现功能:fun()可以接收任何的子类对象,这样无论子类如何增加,fun()方法都不用做任何的改变,因为一旦发生向上转型关系后,调用的方法一定时被子类覆写过的方法。
public class HelloWord{ public static void main(String[] args) { fun(new B()); fun(new C()); } public static void fun(A a){ a.fun1(); } } ============================================== B-fun1() C-fun1()
6,instanceof关键字
instanceof关键字用来判断一个对象到底是那个类的实例。
class A{ } class B extends A{ } public class HelloWord{ public static void main(String[] args) { A a1 = new B(); System.out.println(a1 instanceof A); System.out.println(a1 instanceof B); A a2 = new A(); System.out.println(a2 instanceof A); System.out.println(a2 instanceof B); } } =========================================== true true true false
通过子类实例化的对象同时也是子类和父类的实例,所以可以直接进行向上或向下转型,但如果直接使用了父类实例化本类对象,则一定就时子类实例了,是不能转型的。
7,Object类
7.1,基本作用
Object类是所有类、数组、枚举类的分类,Java允许把任何类型的对象赋值给Object类型变量。当定义一个类时没有使用extends关键字为它显式指定父类,则该类默认继承Object父类。class Person extends Object()的效果跟class Persion()一样。
主要方法:
方法名称 类型 描述 public Object() 构造 构造方法 public boolean equals(Object obj) 普通 对象比较 public int hashCode() 普通 获取Hash码 public String toString() 普通 对象打印时调用 设计一个类最好重写Object类中的equals()、hashCode()、toString() 3个方法。
7.2、主要方法
toString()
class A{ } public class HelloWord{ public static void main(String[] args) { A a = new A(); System.out.println(a); System.out.println(a.toString()); } } ================================================ Package2.A@4554617c Package2.A@4554617c
加不加toString()最终的输出结果是一样的,也就是说对象输出时一定会调用Object类中的toString()方法打印内容。所以利用此特性就可以通过toString()取得一些对象的信息。
class A{ public String toString(){ return "Hello Word"; } } public class HelloWord{ public static void main(String[] args) { A a = new A(); System.out.println(a); System.out.println(a.toString()); } } ============================================ Hello Word Hello Word
覆写Object类中的toString()方法,这样直接输出对象时调用的时被子类覆写过的toString()方法。
equals():equals()方法的功能就是对象的比较(String是Object类的子类,而String是覆写了Object中的equals()方法,直接比较字符串的值),Object提供的equals()方法实际上比较的是栈内存中的堆内存地址,并不能对内容比较。
//jdk源码 //Object中的equals() public boolean equals(Object obj) { return (this == obj); } //String中的equals() public boolean equals(Object anObject) { if (this == anObject) { return true; } if (anObject instanceof String) { String anotherString = (String)anObject; int n = value.length; if (n == anotherString.value.length) { char v1[] = value; char v2[] = anotherString.value; int i = 0; while (n-- != 0) { if (v1[i] != v2[i]) return false; i++; } return true; } } return false; }
7.3,接收任意应用类型的对象
既然Object类是所有对象的父类,则所有的对象都可以向Object进行转换,在这其中也包含了数组和接口类型,即一切的应用数据类型都可以使用Object进行接收。
public class HelloWord { public static void main(String[] args) { int temp[] = {1,3,5,7,9}; Object obj = temp; print(obj); } public static void print(Object o){ if (o instanceof int[]){ int x[] = (int[])o; for (int i=0;i<x.length;i++){ System.out.println(x[i]); } } } } ============================================= 1 3 5 7 9
7.4,对象克隆
在Java中,对象的克隆可以通过实现Cloneable接口和重写Object类的clone()方法来实现。
在需要进行克隆的类中实现Cloneable接口,该接口没有任何方法,仅用于标识该类可以进行克隆。
在该类中重写Object类的clone()方法,该方法是浅克隆,即只复制了对象本身,而不会复制对象所包含的引用类型成员变量。如果需要实现深克隆,需要在clone()方法中对引用类型成员变量进行递归复制。
调用clone()方法来进行克隆,可以使用Object类的clone()方法,但该方法是protected类型,需要在该类中实现一个public类型的clone()方法。
8,包装类
8.1,包装类介绍
Java 是一个近乎纯洁的面向对象编程语言,但是为了编程的方便还是引入了基本数据类型,但在Java中一切皆对象,这样就出现了一个矛盾:从数据类型的划分中可以知道Java中的数据类型分为基本数据类型和引用数据类型,但是基本数据类型怎么能够称为对象呢?为了解决此问题就需要将这8种基本数据类型包装成一个类的形式,那么这就是包装类的作用。
基本数据类型 包装类 基本数据类型 包装类 int Integer char Character short Short byte Byte long Long boolean Boolean float Float double Double (1)Integer、Byte、Float、Double、Short、Long都属于Number类的子类。
(2)Character、Boolean属于Object的直接子类。
8.2,自动装箱和自动拆箱
自动装箱:就是可以把一个基本类型变量直接赋值给对应的包装类变量或者赋给Object变量。
自动拆箱:允许直接把包装类对象直接赋值给一个对应的基本类型变量。
//手动 Integer a = new Integer(1); //装箱 int b = a.intValue(); //拆箱
在JDK1.5之后提供了自动的装箱及拆箱操作,大大简化了基本类型变量和包装类对象之间的转换过程。值得指出的是,进行自动装箱和自动拆箱时必须注意类型匹配。
//自动 Integer a = 1; //装箱 int b = a; //拆箱
8.3,进制转换
最常用:字符串与基本数据类型的转换、进制转换。
String a = "30"; int b = Integer.parseInt(a); String c = "30.3"; double d = Double.parseDouble(c);
Integer.toBinaryString(int i) 将i以二进制形式输出出来 Integer.toOctalString(int i)将i以八进制形式输出出来 Integer.toHexString(int i)将i以十六进制形式输出出来