10. 面向对象
10.10 Object类
含义:所有类的基类/超类,一个类没有明确继承的类,默认继承Object
Object作为所有类的父类,定义了几个方法,方便子类去重写
10.10.1 equals()方法
equals:比较两个对象在内存地址是否相同。
Object类中的equals方法:
public boolean equals(Object obj) { return (this == obj); }
示例:
Object obj1 = new Object();//0x001 Object obj2 = new Object();//0x002 System.out.println(obj1.equals(obj2));//false System.out.println(obj1 == obj2);//false
当Object中的equals方法中的比较方式不在适用于子类时,由子类重写(例如String类)
拓展:
1.==两边是基本数据类型时,比较的是具体内容,为引用数据类型时,比较的是地址。
public static void main(String[] args) { String str1=new String("aaa"); String str2=new String("aaa"); String str3="ccc"; String str4="ccc"; System.out.println(str1==str2); //false System.out.println(str1.equals(str2)); //true System.out.println(str3==str4); //true System.out.println(str3.equals(str4)); //true }
注意:在常量池中只有一个"ccc",即str3和str4指向的地址相同。而str1和str2的地址是不相同的(每次new会在堆中开辟新空间)在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; }
10.10.2 hashCode()方法
含义:获取对象的hash值:
hash值-系统利用对象的内存地址+散列算法获取的一个值
注:hash值不等于内存地址示例:
Object obj1 = new Object(); Object obj2 = new Object(); System.out.println(obj1.hashCode()); System.out.println(obj2.hashCode());
10.10.3 getClass()方法
含义:获取类的字节码文件对象
public class A { String str1; String str2; int num; private void method01(){ System.out.println("method01方法"); } public void method02(){ method01(); System.out.println("method02方法"); } }
public static void main(String[] args) { A a = new A(); //获取A类的字节码文件对象 Class<? extends A> c = a.getClass(); //获取A类中所有属性 Field[] declaredFields = c.getDeclaredFields(); for (Field field : declaredFields) { System.out.println(field); } System.out.println(""); //获取A类中所有的方法 Method[] declaredMethods = c.getDeclaredMethods(); for (Method method : declaredMethods) { System.out.println(method); } } /* java.lang.String com.dream.day6.A.str1 java.lang.String com.dream.day6.A.str2 int com.dream.day6.A.num public void com.dream.day6.A.method02() private void com.dream.day6.A.method01() */
10.10.4 toString()方法
含义:获取对象的字符串表示
Object中的toString方法:
public String toString() { return getClass().getName() + "@" + Integer.toHexString(hashCode()); }
Object obj1 = new Object(); //java.lang.Object@15db9742 System.out.println(obj1.toString());
每个子类都有不同的属性,重写toString直接打印该对象中所有的属性,方便观察数据
重写示例:String类
public String toString() { return this; }
System.out.println("abcdefg".toString()); //abcdefg
10.11 final --最终的
含义:最终
作用:
修饰类:该类不能被继承
修饰方法:该方法不能被重写
修饰变量:变成常量,不能重新赋值
常量的命名规则:全大写,单词之间用下划线隔开
常量的声明周期:存在常量池中,直到项目结束才会被销毁
10.12 abstract --抽象类及抽象方法
//抽象类
public abstract class 类名{
//抽象方法
public abstract void method();
}
抽象方法交给非抽象的子类去实现(重写)
应用场景:当一个方法必须在父类中出现,但是这个方法又不好实现,就把该方法变成抽象方法,交给非抽象的子类去实现
示例:
1.抽象父类Person —定义有抽象方法eat()
public abstract class Person { private String name; private char sex; private int age; public Person() { } public Person(String name, char sex, int age) { this.name = name; this.sex = sex; this.age = age; } public String getName() { return name; } public void setName(String name) { this.name = name; } public char getSex() { return sex; } public void setSex(char sex) { this.sex = sex; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } @Override public String toString() { return "Person [name=" + name + ", sex=" + sex + ", age=" + age + "]"; } public abstract void eat(); }
2.抽象子类Chinese —定义有抽象方法hoppy()
(子类Chinese虽然继承抽象类Person但是他本身也是抽象类,所以可以不实现父类中的抽象方法eat()。)
public abstract class Chinese extends Person{ String id; public Chinese(String name, char sex, int age, String id) { super(name, sex, age); this.id = id; } public Chinese() { } public String getId() { return id; } public void setId(String id) { this.id = id; } @Override public String toString() { return "Chinese [id=" + id + ", toString()=" + super.toString() + "]"; } public abstract void hoppy(); //定义抽象方法。 }
3.子类SiChuan (四川) --继承父类Chinese,需要实现两个抽象方法 eat和hoppy
public class SiChuan extends Chinese{ public SiChuan() { } public SiChuan(String name, char sex, int age, String id) { super(name, sex, age, id); } @Override public void hoppy() { System.out.println(super.getName()+"打麻将"); } @Override public void eat() { System.out.println(super.getName()+"吃火锅"); } @Override public String toString() { return "SiChuan [toString()=" + super.toString() + "]"; } }
public static void main(String[] args) { SiChuan sc=new SiChuan("小明",'女',18,"123456789"); sc.eat(); sc.hoppy(); System.out.println(sc); } /* 小明吃火锅 小明打麻将 SiChuan [toString()=Chinese [id=123456789, toString()=Person [name=小明, sex=女, age=18]]] */
注:
- 抽象类不能直接创建对象,但可以创建匿名内部类,需要重写抽象方法。
Chinese c = new Chinese() { @Override public void eat() { } @Override public void hobby(){ } }; //1.创建匿名类,继承Chinese,重写eat、hobby方法 //2.创建匿名类的对象 //3.赋值给父类的引用
- 抽象类可以有构造方法
- 抽象类中可以有非抽象方法(成员方法和静态方法)
- 抽象类中可以没有抽象方法,但是毫无意义,与普通类相同
- 如果父类是抽象类,则子类不是抽象类时,才必须实现父类的抽象方法
10.13 interface --接口
含义:特殊的抽象类
注意:
- JDK1.7时,接口中只能有静态常量(static final)和抽象方法(abstract)
- JDK1.8开始,接口中可以允许存在有静态方法(static)和默认方法(default)
应用场景:对实现类进行行为上的规范。
public interface I1 { //默认添加public static final int i = 100; //默认添加abstract public void method01(); public void method02(); //默认方法 default void defaultMethod(){ System.out.println("接口中的默认方法"); } //静态方法 public static void staticMethod(){ System.out.println("接口中的静态方法"); } }
抽象类 vs 接口
抽象类:成员变量、静态变量、静态常量、成员方法、静态方法、抽象方法
接口:静态常量、抽象方法; 静态方法、默认方法(JDK1.8)
注:
- 一个类可以实现多个接口,但只能继承一个父类。
public class A implements I1,I2,I3 { @Override public void method_3() { } @Override public void method_2() { } @Override public void method_1() { } }
- 一个接口不可以实现其他接口,但可以继承,且可以继承多个接口。
public interface I1 extends I2,I3{ //I2和I3也是接口类 public void method_1(); }
接口里面的方法不一定都是抽象的,JDK1.7时接口里只能有抽象方法,但JDK1.8及以后,接口可以有抽象方法和默认方法
接口解决了类的单继承问题。
类能够同时实现继承和多接口。
接口可以new对象与抽象类相同,只能创建匿名内部类。
类 - 接口的关系:
类 - 类:单继承
类 - 接口:多实现
接口 - 接口:多继承
10.14 多态
在面对需求升级或是迭代时,常常需要修改程序,此时应该满足OCP原则。
OCP - 开闭原则:
O - open :在需求升级时,对于创建类是欢迎的
C - close:在需求升级时,改变原有代码是拒绝的注:需求升级时,尽量不要改变以前的类,否则容易出bug
10.14.1 类的多态
类的多态通过继承和向上转型实现,使子类对象指向父类引用(即,父类引用中储存的是子类对象在堆中的地址。)
示例:
//定义抽象父类 交通工具类 public abstract class Vehicle { public abstract void start(); public abstract void stop(); }
//子类 自行车类 public class Bike extends Vehicle{ public void start(){ System.out.println("启动自行车"); } public void stop(){ System.out.println("关闭自行车"); } }
//子类 小汽车类 public class Car extends Vehicle{ public void start(){ System.out.println("启动小汽车"); } public void stop(){ System.out.println("关闭小汽车"); } }
//测试方法 public static void main(String[] args) { Teacher t = new Teacher(); Vehicle v= new Car(); Vehicle v1= new Bike(); t.open(v); System.out.println("欣赏沿途风景"); t.close(v); t.open(v1); System.out.println("欣赏沿途风景"); t.close(v1); } /* 启动小汽车 欣赏沿途风景 关闭小汽车 启动自行车 欣赏沿途风景 关闭自行车 */
10.14.2 接口的多态
接口的多态是通过实现类和向上转型完成,指实现类的对象指向接口的引用,在接口的引用中存放的是实现类对象在堆中开辟空间的地址。
示例:
//Usb接口 public interface IUsb { public void usb(); }
//实现类 鼠标类 public class Mouse implements IUsb { @Override public void usb() { System.out.println("鼠标点点点"); } }
//电脑类 public class Computer { public void useusb(IUsb u){ u.usb(); } }
//测试方法 public static void main(String[] args) { Computer c =new Computer(); IUsb u =new Mouse(); c.useusb(u); } /* 鼠标点点点 */
10.15 对象转型
10.15.1 向上转型
含义:子类类型 转 父类类型
1. 可以调用父类非私有化属性
2. 可以调用父类非私有化方法
3. 可以调用子类重写父类的方法
4. 不可以调用子类属性和方法注:向上转型就是多态。(详细示例可以参照10.14 --多态)
缺点:
不可以调用子类自己的属性和方法。
10.15.2 向下转型
含义:父类类型 转 子类类型
注:向下转型为强制转换,转换前需要使用instanceof进行判断,否则容易出现类型转换异常–ClassCastException 。
(使用向下转型时,此对象应经过一次向上转型)
示例:
//父类 动物类 public class Animal { public void method(){ System.out.println("Animal的方法"); } }
//子类 狗类 public class Dog extends Animal{ @Override public void method() { System.out.println("Dog~~旺旺旺"); } }
//子类 猫类 public class Cat extends Animal{ @Override public void method() { System.out.println("Cat~~喵喵喵"); } }
//测试方法 public static void main(String[] args) { Animal animal = new Dog(); //向上转型 if(animal instanceof Dog){ //先判断 Dog dog = (Dog) animal; dog.method(); } if(animal instanceof Cat){ Cat cat = (Cat) animal; cat.method(); } } /* Dog~~旺旺旺 */
10.16 内部类
知识点:内部类
含义:一个类里面声明一个类
10.16.1 成员内部类
特点:可以调用外部类中所有的属性
示例:
public class A { public String str1="aaa"; private String str2="bbb"; protected String str3="ccc"; static String str4="ddd"; final String STR5="eee"; public class A1{ public void method(){ System.out.println("成员内部类的方法"); System.out.println(str1); System.out.println(str2); System.out.println(str3); System.out.println(str4); System.out.println(STR5); } } }
10.16.2 静态内部类
特点:只能到调用外部类的静态属性
示例:
public class B { static String str="111"; static class B1{ public void method(){ System.out.println("静态内部类的方法"); System.out.println(str); } } }
10.16.3 接口内部类
注意:接口内部类底层就是静态内部类
示例:
public interface IC { String str="111"; class C1{ public void method(){ System.out.println("接口内部类的方法"); System.out.println(str); } } }
10.16.4 局部内部类
含义:放在成员方法中的类
普通示例:
public class D { public String str="aaa"; public void functians(){ class D1{ public void method(){ System.out.println("局部内部类的方法"); System.out.println(str); } } D1 d1=new D1(); d1.method(); } }
深入示例:
public class Outter { public Object function(){ //如果局部内部类中使用到该变量,JDK1.8开始该变量自动变成常量(JDK1.8之前需要手动添加final) //因为防止i在该方法结束后才使用 int i = 100; //局部内部类 class Inner{ @Override public String toString() { return i+""; } } //创建局部内部类的对象 Inner inner = new Inner(); return inner; } }
对以上四类进行综合测试示例:
public static void main(String[] args) { //调用成员内部类的方法。 A1 a1 = new A().new A1(); a1.method(); //调用静态内部类的方法。 B1 b1 = new B.B1(); b1.method(); //调用接口内部类的方法。 C1 c1 = new IC.C1(); c1.method(); //调用局部内部类的方法 D d = new D(); d.functians(); //创建外部类的对象 Outter outter = new Outter(); //调用方法 Object obj = outter.function(); System.out.println(obj); } /* 成员内部类的方法 aaa bbb ccc ddd eee 静态内部类的方法 111 接口内部类的方法 111 局部内部类的方法 aaa 100 */
10.16.5 匿名内部类
匿名内部类分 有匿名子类(父类或者抽象父类)和 匿名实现类(接口)。
//1.创建匿名实现类,实现I1中的method //2.创建匿名实现类的对象,指向接口的引用 I1 i1 = new I1() { @Override public void method() { } }; //1.创建匿名子类,继承A,重写method //2.创建匿名子类对象,赋值给父类的引用 A a = new A() { @Override public void method() { } };
详细示例和图解可以见 : 10.12 --抽象类 —> 注 —> 1.