一、继承性的好处:
二、继承性的格式:class A extends B{}
三、Java中关于继承性的规定:
四
重写:子类继承父类以后,可以对父类中同名同参的方法,进行覆盖操作
应用:重写以后,当创建子类对象以后,通过子类对象调用子父类中的同名同参数的方法时,实际执行的时子类重写父类的方法。
重写的规定:
注:子类和父类的同名同参数的方法要么都声明为非static的(考虑重写),要么都声明为static的(不是重写)。
-因为静态的方法无法被覆盖,随着类的加载而加载
this和super的区别
多态性,是面向对象中最重要的概念,在Java中的体现:
Java引用变量有两个类型:编译时类型和运行时类型。编译时类型由声明该变量时使用的类型决定,运行时类型由实际赋给该变量的对象决定。简称:编译时,看左边;运行时,看右边。
若编译时类型和运行时类型不一致,就出现了对象的多态性(Polymorphism)
多态情况下,“看左边”:看的是父类的引用(父类中不具备子类特有的方法)
“看右边”:看的是子类的对象(实际运行的是子类重写父类的方法)
多态的使用:虚拟方法调用
多态性的使用前提:
对象的多态性,只适用于方法,不适用于属性(编译和运行都看左边)
补充:
例:
package exer; /* * 练习: * 1.若子类重写了父类方法,就意味着子类里定义的方法彻底覆盖了父类里的同名方法, * 系统将不可能把父类里的方法转移到子类中:编译看左边,运行看右边 * * 2.对于实例变量则不存在这样的现象,即使子类里定义了与父类完全相同的实例变量, * 这个实例变量依然不可能覆盖父类中定义的实例变量:编译运行都看左边 * * */ 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 FieldMethodTest { 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(b == s);// true System.out.println(b.count);// 10 //这里20是因为多态性调用了子类的方法,然后子类的方法调用了子类的属性 b.display();// 20 } }
多态性其实在main()方法中没什么好处,就是用父类的引用调用子类的方法,而且只能调用父类中的方法和属性,如果子类有重写的方法就会覆盖掉父类的方法。但无法调用子类特有的方法。综上,多态调用的只能是父类的特有方法、属性和子类重写的方法。
但多态真正用的地方是在形参部分,只需要写一个父类的,然后调用的时候需要它子类的哪个功能就可以直接调用那个内容,就比如数据库连接、抽象类和接口
谈谈你对多态性的理解?
实现代码的通用性
Object类中定义的public boolean equals(Object obj){ }
JDBC:使用Java程序操作(获取数据库连接、CRUD)数据库(MySQL、Oracle、DB2、SQL Server)
抽象类、接口的使用肯定体现了多态性。(抽象类、接口不能实例化)
多态是编译时行为还是运行时行为?
证明如下:
package java5; //解释多态性是运行时行为 import java.util.Random; //面试题:多态是编译时行为还是运行时行为? //证明如下: 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 Dog extends Animal { public void eat() { System.out.println("Dog eat bone"); } } class Sheep extends Animal { public void eat() { System.out.println("Sheep eat grass"); } } public class InterviewTest { public static Animal getInstance(int key) { switch (key) { case 0: return new Cat(); case 1: return new Dog(); default: 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(); } }
例二:笔试题
package exer; //考查多态的笔试题目: public class InterviewTest1 { public static void main(String[] args) { Base1 base = new Sub1(); base.add(1, 2, 3); //sub_1 Sub1 s = (Sub1)base; s.add(1,2,3); //sub_2 } } 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"); } }
方法的重载与重写
问题导入:
向下转型:使用强制类型转换符。
Person p2 = new Man(); Man m1 = (Man) p2; m1.earnMoney();
由上图可知,向下转型是多态的逆过程
注意:使用强转时,可能出现ClassCastException的异常。
几种情况:
编译时通过,运行时不通过
//举例一 Person p4 = new Woman(); Man m4 = (Man)p4; //举例二 Person p5 = new Person(); Man m5 = (Man)p5;
编译通过,运行时也通过
Object obj = new Woman(); Person p = (Person)obj;
编译不通过
Man m6 = new Woman();
为了在向下转型时不出现ClassCastException的异常,引入了instanceof关键字
a instanceof A
:判断对象a是否是类A的实例。如果是,返回true;如果不是,返回false。
Person p2 = new Man(); if (p2 instanceof Woman) { // false Woman w1 = (Woman) p2; w1.goshopping(); System.out.println("*******Woman*******"); }
如果a instanceof A返回true,且类B时类A的父类,则a instanceof B也返回true。
要求a所属的类与类A必须是子类和父类的关系,否则编译错误。
方法名 | 返回值类型 | 描述 |
---|---|---|
clone() | protected Object | 创造并返回当前复制的对象,直接对象.clone()进行调用,返回的类型要进行强转 |
equals(Object obj) | boolean | 指示一些其他对象是否等于此对象 |
finalize() | protected void | 当垃圾收集确认不再有对该对象的引用时,垃圾收集器在对象上调用此方法(这个方法一般不会自己主动去调用,一般是垃圾回收机制自动调用) |
getClass() | class<?> | 获取当前对象的所属类(返回此对象的类名) |
hashCode() | int | 返回对象的hash值(哈希码值) |
toString() | String | 返回对象的字符串形式 |
notify() | void | 唤醒正在等待对象监视器的单个线程 |
notifyAll() | void | 唤醒正在等待对象监视器的所有线程 |
wait() | void | 导致当前线程等待,直到另一个线程调用该对象的 notify() 方法或 notifyAll() 方法。 |
wait(long timeout) | void | 导致当前线程等待,直到另一个线程调用 notify() 方法或该对象的 notifyAll() 方法,或者指定的时间已过。 |
wait(long timeout, int nanos) | void | 导致当前线程等待,直到另一个线程调用该对象的 notify() 方法或 notifyAll() 方法,或者某些其他线程中断当前线程,或一定量的实时时间。 |
import org.junit.Test; public class ReviewTest { //数组也作为Object类的子类出现,可以调用Object类中声明的方法 @Test public void test1() { int[] arr = new int[] { 1, 2, 3 }; print(arr);// [I@443b7951 System.out.println(arr.getClass()); //class [I System.out.println(arr.getClass().getSuperclass());// class java.lang.Object } public void print(Object obj) { System.out.println(obj); } }
final、finally、finalize的区别?
== 和 equals() 区别
一、回顾 == 的使用:
补充:= = 符号使用时,必须保证符号左右两边的变量类型一致。
二、equals() 的使用
是一个方法,而非运算符
只能适用于引用数据类型(因为基本数据类型无法调用方法,引用数据类型都是类的实例化,可以调用方法)
Object类中的equals()的定义:
public boolean equals(Object obj) { return (this == obj); }
说明:Object类中定义的equals()和 == 的作用是相同的:比较两个对象的地址值是否相同。即两个引用是否指向同一个对象实体
像String、Date、File、包装类等都重写了Object类中的equals()方法。重写以后,比较的不是两个引用的地址是否相同,而是比较两个对象的"实体内容"是否相同。
通常情况下,我们自定义的类如果使用equals()的话,也通常是比较两个对象的"实体内容"是否相同。那么,我们就需要对Object类中的equals()进行重写,重写的原则:比较两个对象的实体内容是否相同。
public class EqualsTest { public static void main(String[] args) { // 基本数据类型 int i = 10; int j = 10; double d = 10.0; System.out.println(i == j);// true System.out.println(i == d);// true char c = 65; char c1 = 'A'; char c2 = 10; System.out.println(c); // A System.out.println(c == c1);// true System.out.println(c2 == i);// true // 引用数据类型 Customer cust1 = new Customer("Tom", 21); Customer cust2 = new Customer("Tom", 21); System.out.println(cust1 == cust2); // false String str1 = new String("cxdthd"); String str2 = new String("cxdthd"); System.out.println(str1 == str2);// false System.out.println("******************************"); System.out.println(cust1.equals(cust2));// false,重写后为true System.out.println(str1.equals(str2));// true Date date1 = new Date(123); Date date2 = new Date(123); System.out.println(date1.equals(date2));// true } }
如何在自己创建的类中重写equlas()方法?
参考String类中的equals()方法
public boolean equals(Object anObject) { if (this == anObject) { return true; } if (anObject instanceof String) { String anotherString = (String)anObject; //这里的value指的是当前对象(this)的字符串长度,因为String类型在底层是以char型数组的形式存在 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; }
手动实现equals()方法
//重写的原则:比较两个对象的实体内容(即:name和age)是否相同 //手动实现 @Override public boolean equals(Object obj) { //第一步,比较两个对象的地址值是否相等 if (this == obj) { return true; } //第二步,判断对象是否是this对象的实例 if (obj instanceof Customer) { // 方法一 // if (this.name.equals(((Customer) obj).name) && this.age == // ((Customer) obj).age) { // return true; // } // 方法二 return this.name.equals(((Customer) obj).name) && this.age == ((Customer) obj).age; } return false; }
Eclipse自动实现equals()方法重写
// 自动生成的equals()方法 @Override public boolean equals(Object obj) { if (this == obj) return true; if (obj == null) return false; if (getClass() != obj.getClass()) return false; Customer other = (Customer) 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; }
区别手动写的自动生成的equals()
@Test public void test2(){ Person p = new Person("Tom",18); Man m = new Man("Tom",18); System.out.println(p.equals(m));//true ---> false System.out.println(p.getClass()); }
当我们输出一个对象的引用时,实际上就是调用当前对象的toString()
Object类中toString()的定义:
public String toString() { //返回他的hashCode值,并转换为十六进制 return getClass().getName() + "@" + Integer.toHexString(hashCode()); }
像String、Date、File、包装类都重写了Object类中的toString()方法。使得在调用对象的toString()时,返回"实体内容"信息
自定义类也可以重写toString()方法,当调用此方法时,返回对象"实体内容"。
注意点:
@Test public void test3(){ String s = "abc"; System.out.println(s);//abc System.out.println(s.toString());//abc s = "null"; System.out.println(s);//null为字符串 System.out.println(s.toString());//null s = null; System.out.println(s); //null为空 System.out.println(s.toString());//NullPointerException }
public void print(String s) { if (s == null) { s = "null"; } write(s); }
基本数据类型包 —>包装类 —装箱
调用包装类的构造器
int num1 = 10; Integer integer = new Integer(num1); Integer integer2 = new Integer("123"); //字符串形式也能自动识别 //错误写法,会报异常:NumberFormatException Integer integer3 = new Integer("123abc"); //只能纯数字,不能有其他符号 Float float1 = new Float(12.3f); Float float2 = new Float("12.3"); Boolean boolean1 = new Boolean(true); // true Boolean boolean2 = new Boolean("tRue"); // true:忽略大小写 Boolean boolean3 = new Boolean("abc"); // false
包装类 —>基本数据类型 —拆箱
调用包装类Xxx的xxxValue()
Integer in1 = new Integer(12); int number1 = in1.intValue(); Float f1 = new Float(12.3); float f2 = f1.floatValue();
JDK 5.0新特性:自动装箱与自动拆箱
// 自动装箱:不需要新new一个构造器,可以直接赋给对象 // 基本数据类型 --->包装类 int num2 = 10; Integer in1 = num2; boolean b1 = true; Boolean b2 = b1; // 自动拆箱:不需要调用包装类,也是直接赋值给基本数据类型 // 包装类 --->基本数据类型 int num3 = in1;
基本数据类型、包装类 —>String类型
""
做连接运算就可以转为String的// 方式一:连接运算 int num1 = 10; String str1 = num1 + ""; System.out.println(str1 + 1);// 101
valueOf(Xxx xxx)
// 方式二:调用String重载的valueOf(Xxx xxx) float f1 = 12.3f; String str2 = String.valueOf(f1); System.out.println(str2 + 1);// 12.31 Double d1 = new Double(12.4); String valueOf = String.valueOf(d1);
String类型—>基本数据类型、包装类
parseXxx(String s)
方法String str1 = "123"; System.out.println(str1 + 1);// 1231 // 错误的情况:只有子父类关系的情况下才可以使用强转 // int i1 = (int)str1; // Integer integer = (Integer)str1; // 两步:先转成Integer包装类,再转成int型(自己写的) Integer integer = new Integer(str1); int i1 = integer; System.out.println(i1 + 1);// 124 // 一步到位:调用包装类的parseXxx(String s)方法 int parseInt = Integer.parseInt(str1); //这里str1如果有数字以外的内容,会报异常:NumberFormatException System.out.println(parseInt + 2);// 125 String str2 = "trUe"; boolean parseBoolean = Boolean.parseBoolean(str2); System.out.println(parseBoolean);// true
注意:
boolean型变量的默认初始化值为false,Boolean是一个包装类,所有没new对象的时候是个null
class Order { boolean isMale; Boolean isFemale; } @Test public void test(){ Order order = new Order(); System.out.println(order.isMale); //false System.out.println(oeder.isFemale); //null }
例一
@Test public void test1() { // 三元运算要求数据类型要统一,所以int型会自动类型提升为double型 Object o1 = true ? new 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 test3() { 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了。目的:提高效率 // 超出这个范围就需要新new对象,所以128为false 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 }
Integer.class里内部类IntegerCache的源码
private static class IntegerCache { static final int low = -128; static final int high; static final Integer cache[]; static { // high value may be configured by property int h = 127; String integerCacheHighPropValue = sun.misc.VM.getSavedProperty("java.lang.Integer.IntegerCache.high"); if (integerCacheHighPropValue != null) { try { int i = parseInt(integerCacheHighPropValue); i = Math.max(i, 127); // Maximum array size is Integer.MAX_VALUE h = Math.min(i, Integer.MAX_VALUE - (-low) -1); } catch( NumberFormatException nfe) { // If the property cannot be parsed into an int, ignore it. } } high = h; cache = new Integer[(high - low) + 1]; int j = low; for(int k = 0; k < cache.length; k++) cache[k] = new Integer(j++); // range [-128, 127] must be interned (JLS7 5.1.7) assert IntegerCache.high >= 127; } private IntegerCache() {} }