目录
复习重写 |
package abstract_class; class Person { //父类 parent class public void fun(){ this.test(); } private void test(){ // The test method is private,Subclasses don't even know test exists. //so ,unable to rewrite. //private method only visible inside the current class. System.out.println("Person test method"); } } class Student extends Person{//子类 subclasses public void test(){ System.out.println("test method of student class "); } } public class Test{ public static void main(String[] args) { new Student().fun(); //The fun method only exists in parent class. } }
package abstract_class; class Person { //父类 parent class public void fun(){ this.test(); } void test(){// After the elimination of private. System.out.println("Person test method"); } } class Student extends Person{//子类 subclasses //The subclass is overridden 此时子类被重写了 public void test(){ System.out.println("test method of student class "); } } public class Test{ public static void main(String[] args) { new Student().fun(); //The fun method only exists in parent class. } }
code output "test method of student class "!!
子类和父类有不同的标记符号:
子类向上,父类向下;
方法重写的类型的返回值必须严格相同: |
毫无相关的 两种类型不能作为方法重写的返回值,比如受父类中的是int而子类中的是boolean,这就不可以返回,不算方法重写,会报成不兼容。
这时子类使用student作为返回值,父类使用Person作为返回值是可以的,因为这个student本来就是Person, student is a person。
package abstract_class;
class Person { //父类 parent class
public void fun(){
this.test();
}
Person test(){
System.out.println("Person test method");
return new Person();
}
}
class Student extends Person{//子类 subclasses
public Student test(){
System.out.println("test method of student class ");
return new Student();
}
}
public class Test{
public static void main(String[] args) {
new Student().fun();
//The fun method only exists in parent class.
}
}
如果反过来父类中是返回Student,子类中返回person 呢?
会报错:“attempting to use incompatible return type” 尝试使用不兼容的类型。
原因这时是向下转型,而不是向上转型,person is not a student,
package abstract_class;
class Person { //父类 parent class
public void fun(){
this.test();
}
Student test(){
System.out.println("Person test method");
return new Student();
}
}
class Student extends Person{//子类 subclasses
public Person test(){
System.out.println("test method of student class ");
return new Person();
}
}
public class Test{
public static void main(String[] args) {
new Student().fun();
//The fun method only exists in parent class.
}
}
一般情况下,重写方法的返回值类型不一定和父类相同,但是建议写成相同。
抽象类 |
抽象类只是比普通类多了一些抽象的方法,可以是0个或者n个。
抽象方法所在的类必须是抽象类,子类若继承了抽象类,必须重写所有的抽象方法(但是子类必须是普通类)。
抽象方法是使用abstract关键字声明,没有函数实现的方法。
java中定义抽象类或者抽象方法使用abstract关键字;
抽象方法的实现要在子类中去实现。
1只要使用抽象类必须用abstract声明为抽象类 此时的print只有方法的声明,没有方法体。所谓的方法体就是{} | 没有方法体的方法不一定就是抽象方法。 |
本地方法 | native表示本地的,这个方法不是抽象方法,具体实现由C++写好的方法完成。 |
2若一个类使用abstract声明为抽象类,无法直接通过该类实例化对象,哪怕该类中一个抽象方法都没有。当一个类是抽象类,不管有没有抽象方法,这个类本身就是一个抽象的概念,没法具体到某个特定实例。 | 抽象类不能自己实例化对象,只能通过子类向上转型转为抽象父类的引用。 Sharp sharp =new Sharp(); // false 人类是一个抽象的概念 Person person= new Person(); // false Person person=new China(); // true |
3子类继承了抽象类就必须强制要求重写所有的抽象方法(子类是个普通类),一个子类只能extends一个抽象类 | 类“C”必须声明为抽象,或者实现抽象方法“printB()” |
如果B中没有重写A,那么普通类C要实现两次 | |
如果B重写了A | |
4.抽象类虽然没法直接实例化对象,但是可以存在构造方法,子类在实例化对象的时候仍遵从继承的原则。先调用父类的构造方法,而后调用子类的构造方法。 | 先调用父类,然后this.print()调用被子类重写的方法,此时num==0; |
到底啥时候会用到抽象类,稍微复杂一点,先阶段主要掌握接口类的定义与使用。
若一个需求既可以使用抽象类也可以使用接口,优先使用接口 ,因为抽象类依然是单继承局限。
接口 |
抽象类虽然没法直接实例化对象,但是子类仍然满足 is a 原则,子类和抽象父类之间仍然是一个强的继承树关系,Person 对于China 和Sharp 对于Cycle, China 只能继承Person, Cycle 只能继承 Sharp,而接口是一个混合的概念,他不是一个垂直的方向。接口的使用一般表示两种, |
1.接口表示具备某种能力或者行为,子类在实现接口时不是 is a 关系,而是具备这种行为能力。 比如 游泳 ,Person 满足游泳接口, Dog也能满足游泳的接口,他是一个混合的实现,Duck 也能满足游泳接口,此时游泳接口就是一个表示游泳能力或者行为。 |
2.接口表示一种规范,比如“USB”接口,“5G标准”。 |
接口中只有全局常量和抽象方法,更加存粹的抽象概念,其他东西都没有。
接口使用关键字-interface声明接口,子类使用implement实现接口;
1.USB接口
USB接口表示一种规范,那么显示生活中哪些类使我们接口的子类,鼠标,键盘这种外设都是。
子类使用implements实现接口,必须重写所有的抽象方法。
电脑这个类不属于USB接口的子类,所有带USB线插入到电脑的设备都应该满足USB规范,电脑叫做USB规范的使用者。
其实鼠标和键盘我们都需要各有一个接口,所以这个例子不太好。
代码:鼠标和键盘插入
package usb;
/**
* 接口使用interface关键字定义,只有全局常量(1%会用),和抽象方法(99%是方法)
*/
public interface USB {
// 插入
public abstract void plugIn();
//工作
public abstract void work();
}
package usb;
public class Mouse implements USB{
@Override
public void plugIn() {
System.out.println("安装鼠标驱动");
}
@Override
public void work() {
System.out.println("鼠标开始工作");
}
}
package usb;
public class KeyBoard implements USB{
@Override
public void plugIn() {
System.out.println("安装键盘驱动中");
}
@Override
public void work() {
System.out.println("键盘正常工作");
}
}
package usb;
public class Computer {
public static void main(String[] args) {
Computer computer=new Computer();
Mouse mouse =new Mouse();
//调用fun方法把mouse传入,插入一个鼠标
computer.fun(mouse);
KeyBoard keyBoard=new KeyBoard();
computer.fun(keyBoard);
}
public void fun(USB usb){
usb.plugIn();
usb.work();
}
}
安装鼠标驱动
鼠标开始工作
安装键盘驱动中
键盘正常工作
假如现在这可接口要插入Camera,电脑的fun方法不用变,只要写一个子类,然后创建对象,引用传入就行了,这也是多态。
开闭原则,所有设计模式的核心思想:程序应该的拓展开放,对修改关闭。方便扩展,不能影响已经写好的程序。
接口表现能力!
接口允许多实现,一个类可以具备多种能力 ,同时实现多父接口,若实现多个父接口,子类普通类,需要复写所有抽象方法。
一个类既需要继承一个父类又需要实现多个接口时:
类名 extends 父类 implements 接口1,接口2...{}
兔子狗鸭子
package animal;
public interface ISWim {
public abstract void swim();
}
package animal;
/**
* 具备跑的能力或者行为
*/
public interface IRun {
public abstract void run();
}
package animal;
/**
* 表示具备飞的能力或行为
*/
public interface IFly {
public abstract void fly();
}
package animal;
//兔子只会跑不会游泳
public class Rabbit implements IRun{
@Override
public void run() {
System.out.println("兔子再跑~~");
}
}
package animal;
//Dog会跑也会游泳
public class Dog implements IRun,ISWim{
@Override
public void run() {
System.out.println("狗在跑~~~~");
}
@Override
public void swim() {
System.out.println("狗在游泳~~~~");
}
}
package animal;
//鸭子会跑回游会飞
public class Duck implements IRun,IFly,ISWim {
@Override
public void fly() {
System.out.println("鸭子在飞");
}
@Override
public void run() {
System.out.println("鸭子在跑");
}
@Override
public void swim() {
System.out.println("鸭子在游泳");
}
}
package animal;
public class Test {
public static void main(String[] args) {
//接口也不能直接实例化对象,需要向上转型
IRun run = new Rabbit();
IRun run1 = new Dog();
IRun run2 = new Duck();
ISWim swim = new Dog();
ISWim swim1 = new Duck();
IFly fly = new Duck();
run.run();
run1.run();
run2.run();
swim.swim();
swim1.swim();
fly.fly();
}
}
兔子再跑~~
狗在跑~~~~
鸭子在跑
狗在游泳~~~~
鸭子在游泳
鸭子在飞
由于接口中只会有全局常量和抽象方法,因此接口中 public abstract=>抽象方法 static final=>常量 全都可以省略!!! |
|
兔子再跑~~ 狗在跑~~~~ 鸭子在跑 狗在游泳~~~~ 鸭子在游泳 全局常量 鸭子在飞 |
在定义抽象方法时,abstract和final不能同时出现,抽象方法必须 重写,final修饰后无法重写。 在定义抽象类时,也不可以,抽象类必须有子类,final修饰没有子类。 |
关于接口的命名规范 | 命名接口使用 I 开头,IRun,ISwim |
子类实现一个接口时 | 命名以相应的接口开头,以impl结尾, eg:如果是IRun的子类,以IRunimpl结尾,这个命名不是强制要求。 |
如果子类实现多个父接口不需要使用此规范来命名 | 比如Duck即会游泳有会跑又会飞就不需要使用固定的结尾 |
Object类 |
全名称: 包名.类名 java.lang.Object |
1.Object类是java中所有类默认的父类,无需使用extends来定义,class声明的类都有一个父类,Object类。 因为object类是所有类的父类,使用Object引用来接收所有类型,参数最高统一化。 Object obj1=new Person(); Object obj2=new String(); Object obj3=new Dog(); 都可以通过向上转型变成object,所有类型都可以,这叫参数的最高统一化。 假如说有一个参数实object类型的,void fun(Object obj){},说明他可以接受所有的类型。 |
2.Object是所有类的父类,那么子类可以继承父类的所有方法! 之所以System.out.prientln(任意的引用类型)-->默认都调用toString()方法,是因为Object类存在toString方法。 这就是为什么sout中传入什么类型的对象都能够打印的原因。 还有,方法能不能重写看的是前面的引用有没有这个属性,如果有就调用子类重写后的代码: |
3.java中引用数据类型之间的相等比较使用equals方法! 不能使用“==”比较的是地址; Object类存在equals方法! |
为啥不能用==比较引用?
package object_test;
/**
* 为啥使用== 都是输出false?
*/
public class Object_Test {
public static void main(String[] args) {
Student student1=new Student("显平",80);
Student student2=new Student("怡洲",81);
Student student3=new Student("怡洲",81);
//如果我现在要比较这stu1和stu2是否相等使用"=="
System.out.println(student1==student2);
System.out.println(student3==student2);
}}
class China { }
class Dog {
public String toString(){
return "Dog类的toString方法。";}}
class Student{
private String name;
private int score;
public Student(String name, int score) {
this.name = name;
this.score = score;
}
}
false
false
因为“==”比较的是数值,对于引用数据类型来讲,保存的内容是一个地址,比较的其实就是地址。所以stud1,2,3的地址都不相等,都是false。
若需要比较两个对象的属性值是否相同就需要equals方法: |
此时如果直接调用也是不对的: |
JDK的源代码格式: System.out.println(student2.equals(student3)); 当前这个方法是谁通过哪个对象调用的,我的this就是谁。 equals方法是stud2,obj就是stu3。 这个代码比较的依然是stu2==stu3的地址,所以这个默认的比较方法不能满足我的需要,我就想知道学生的分数是不是相等。 下面我重写这个equals方法:来比较学生的分数属性 |
方法重写指的 除了权限不一样,返回值是boolean。方法的声明要一样。 权限子类大于等于父类,此时只能跟着public。方法名就叫equals,参数类型是Object obj,都要和父类完全一样。 |
this.score==student.score &&this.name.equals(student.name); name属性是String类型,JDK中所有类型属性比较 都使用equals方法(能想到的头重写过了),==比较的是地址。 |
package object_test;
import com.oracle.webservices.internal.impl.internalspi.encoding.StreamDecoder;
/**
* 为啥使用== 都是输出false?
*/
public class Object_Test {
public static void main(String[] args) {
Student student1=new Student("显平",80);
Student student2=new Student("怡洲",81);
Student student3=new Student("怡洲",81);
Student student4=new Student("怡洲",80);
//如果我现在要比较这stu1和stu2是否相等使用"=="
System.out.println(student1==student2);
System.out.println(student3==student2);
System.out.println("----------");
//这个方法其实比较的还是同一个学生的分两次的分数是不是一样。
System.out.println(student2.equals(student3));
System.out.println(student1.equals(student2));
System.out.println(student1.equals(student4));
}}
class China { }
class Dog {
public String toString(){
return "Dog类的toString方法。";}}
class Student{
private String name;
private int score;
public Student(String name, int score) {
this.name = name;
this.score = score;
}
@Override
public boolean equals(Object obj){
//若当前对象就是obj比如:自己等于自己
// System.out.println(student2.equals(student2));
if (this==obj){
return true;
}
/** 走到这儿说明当前地址和obj确实不是一个地址,这个时候
* 若此时obj指向的对象和student压根没有关系,没法比,直接返回false;
* 那我咋判断student对象和object对象有没有关系呢?
*/
if (obj instanceof Student){
/**
* 此时确实有关系了,其不是一个对象
* Object obj=new Student();向下转型取出object
*/
Student student=(Student)obj;
return this.score==student.score &&this.name.equals(student.name);
}
return false;
}
}
练习:自定义Student类,重写equals方法,比较自定义类型的属性值是否相等 |
Object不仅是所有class类的父类,而且JDK对Object做了扩展,Object可以接收所有引用数据类型的对象(接口,数组,类) 比如:Object obj1=new int[ 20 ] //接收了一个整形数组; |
若一个方法参数或者返回值是Object类型,说明该参数或者返回值是任意引用数据类型(数组,类,接口) |
将八大基本类型包装成类,其他说有的引用都可以用Object来接收。 |
接口优先抽象类,因为接口灵活。 |
JDK内置常用接口: java.lang.comparable:当一个类具备了comparable接口,表示这个类具备了可比较的能力。 |
package object_test;
public class Comparable_Test {
public static void main(String[] args) {
/**
* 此时people这个数组中有几个对象,值是多少?
*/
Person [] people=new Person[3];
/**
* people 是一个Person类型的数组,创建了三个Person类型的变量,值都是默认值null
*/
System.out.println(people[0]);
System.out.println(people[1]);
System.out.println(people[2]);
}
}
class Person {
public Person(String name, int age) {
this.name = name;
this.age = age;
}
String name;
int age;
}
output:
null
null
null
创建的实例化
package object_test;
import java.util.Arrays;
public class Comparable_Test {
public static void main(String[] args) {
int [] data=new int[]{
8,1,2};
Arrays.sort(data);
System.out.println(Arrays.toString(data));
}
// Person [] people=new Person[3];
}
当调用Arrays.sort给一个对象数组排序的时候,报转换类型异常 |
package object_test;
import java.util.Arrays;
public class Comparable_Test {
public static void main(String[] args) {
int [] data=new int[]{
8,1,2};
Arrays.sort(data);
System.out.println(Arrays.toString(data));
Person [] people=new Person[]{
new Person("peter鹏哥",30),
new Person("铭哥",18),
new Person("显平",28)
};
// 根据年龄来排序:
Arrays.sort(people);
System.out.println(Arrays.toString(people));
}
}
class Person {
public Person(String name, int age) {
this.name = name;
this.age = age;
}
String name;
int age;
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
为啥要把每个对象转成comparable?
对于data来说数值可以比较大小,但是Person这个类是自定义的,对于编译器来说不知道谁大谁小,像int这样的类型可以比较大小。
要让JDK知道谁大谁小,需要让Person类实现comparable接口,重写抽象方法compeTo,让Person这个类具备比较能力。
package object_test;
import java.util.Arrays;
public class Comparable_Test {
public static void main(String[] args) {
int [] data=new int[]{
8,1,2};
Arrays.sort(data);
System.out.println(Arrays.toString(data));
Person [] people=new Person[]{
new Person("peter鹏哥",30),
new Person("铭哥",18),
new Person("显平",28)
};
// 根据年龄来排序:
Arrays.sort(people);
System.out.println(Arrays.toString(people));
}}
/**
* 让Person implements Comparable 然后Override compareTo
*/
class Person implements Comparable{
public Person(String name, int age) {
this.name = name;
this.age = age;
}
String name;
int age;
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
@Override
public int compareTo(Object o) {
//这里返回一个整形int
if (this==o){
return 0;
}
if (o instanceof Person){
//当前传入对象O就是Person类型的引用,向下转型还原为Person;
//要比较Person对象的大小关系,叫要用到Person 独有的属性,向下转型
Person person=(Person) o;
//此时就是根据年龄大小比较
return this.age-person.age;
}
//报错,抛出异常
throw new IllegalArgumentException("不是Person类型,无法比较。");
}
}
[1, 2, 8]
[Person{name='铭哥', age=18}, Person{name='显平', age=28}, Person{name='peter鹏哥', age=30}]
现在想用自己的sor方法t排序调用compareTo |
package object_test;
import java.util.Arrays;
public class Comparable_Test {
public static void sort(Comparable[]arr){
for (int i = 0; i < arr.length-1; i++) {
for (int j = 0; j < arr.length-i-1; j++) {
//if (arr[j]>arr[j+1])//swap
if (arr[j].compareTo(arr[j+1])>0){
//前一个大于后个,交换j和j+1
Comparable temp=arr[j];
arr[j]=arr[j+1];
arr[j+1]=temp;
}
}
}
}
public static void main(String[] args) {
Person [] people=new Person[]{
new Person("peter鹏哥",30),
new Person("铭哥",18),
new Person("显平",28)
};
// 根据年龄来排序:
sort(people);
System.out.println(Arrays.toString(people));
}}
/**
* 让Person implements Comparable 然后Override compareTo
*/
class Person implements Comparable{
public Person(String name, int age) {
this.name = name;
this.age = age;
}
String name;
int age;
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
@Override
public int compareTo(Object o) {
//这里返回一个整形int
if (this==o){
return 0;
}
if (o instanceof Person){
//当前传入对象O就是Person类型的引用,向下转型还原为Person;
//要比较Person对象的大小关系,叫要用到Person 独有的属性,向下转型
Person person=(Person) o;
//此时就是根据年龄大小比较
return this.age-person.age;
}
//报错,抛出异常
throw new IllegalArgumentException("不是Person类型,无法比较。");
}
}
如何变成降序输出compareTo? |
改成这就行了
克隆接口,java.lang.Cloneable |
克隆羊“多力”
原对象和新对象确实是两个独立的对象,新产生的对象是通过原对象拷贝而来的,属性值和原对象完全一致。
要让一个类具备可复制的能力,实现cloenable接口,重写clone方法。
这种接口里面是Null ,称为标记接口。(标记接口指内部本身没有任何抽象方法,只有打上这个标记的子类才具备克隆的能力,克隆的能力从JVM中来){生活中的标记:猪场养猪,猪长成熟后会被检查身体指标,达到使用标准之后会打上合格的标签。
}JVM在运行时会检查所有实现了Cloneable接口的子类, 赋予其克隆的能力,clone是object方法。通过贴标签把不同的类划分。
package object_test;
public class CloneableTest {
public static void main(String[] args) {
Animal animal1=new Animal("test");
//animal2是同过animal1克隆复制过来的,确实是两个独立的对象,属性值完全相同。
Animal animal2=animal1.clone();
//animal1 和animal2的地址不相等打印的false
System.out.println(animal1==animal2);
System.out.println(animal2.name);
}
}
class Animal implements Cloneable{
String name;
public Animal(String name){
this.name=name;
}
public Animal clone(){
Animal newAnimal=null;
try {
newAnimal=(Animal) super.clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
return newAnimal;
}
}
为甚么我们叫anima1和animal2叫克隆,而animal3和animal1之间没有关系?
只是new animal3的时候恰好等于animal1而已。
比如世界上还要一个属性值和克隆羊多力相同的羊,但是这个羊和多力的基因羊母亲么有关系,而animal2的产生是一样animal1的。
animal3的产生是天然的,和克隆无关。
深浅拷贝 |
快捷键 Alt +Enter
深拷贝:每个对象自身包含的对象也是拷贝出来的。
浅拷贝:每个对象自身包含的对象指向的是同一个对象。
package object_test;
public class DeepCopyTest {
public static void main(String[] args) {
B b1=new B();
B b2=b1.clone();
System.out.println(b1==b2);
System.out.println(b1.a== b2.a);
b1.a.num=100;
System.out.println(b2.a.num);
}
}
class A{
int num;
}
class B implements Cloneable{
A a=new A();
public B clone(){
B newB=null;
try {
newB=(B) super.clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
return newB;
}
}
Deep Clone瞅一眼:了解就行了
让A也实现深拷贝,
package object_test;
public class DeepCopyTest {
public static void main(String[] args) {
B b1=new B();
B b2=b1.clone();
System.out.println(b1==b2);
System.out.println(b1.a== b2.a);
b1.a.num=100;
System.out.println(b2.a.num);
}
}
class A implements Cloneable{
int num;
public A clone(){
A a=null;
try {
a=(A) super.clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
return a;
}
}
class B implements Cloneable{
A a=new A();
public B clone(){
B newB=null;
try {
newB=(B) super.clone();
newB.a=newB.a.clone();
} catch (CloneNotSupportedException e) {
e.printStackTrace();
}
return newB;
}
}
接口和类之间的关系 |
package object_test;
public class TTest {
}
interface IA{
void testA();
}
interface IB{
void testB();
}
//IC接口同时继承了多个父接口,继承了所有抽象方法,
//子类在实现IC接口的时候,必须重写所有的抽象方法
interface IC extends IA,IB{
class CImpl implements IC{
// CImpl如果继承IC需要继承两个方法
//具备了IC和IB的抽象方法
@Override
public void testA() {
}
@Override
public void testB() {
}
}
}
既需要继承一个父类又需要实现多个接口 |
abstract China extends Person implements IB,IA{}
先extens然后implements