接口
接口就像是一种约定 ,我们约定某些英雄是物理系英雄,那么他们就一定能够进行物理攻击。
创建接口
举例:
1 2 3 4 5 6 package charactor; public interface AD { //物理伤害 public void physicAttack(); }
实现接口
实现某个接口,就相当于承诺了某种约定
所以,实现 了AD 这个接口,就必须 提供AD接口中声明的方法physicAttack()
实现 在语法上使用关键字 implements
默认方法
定义
接口也可以提供具体方法了,而不像以前,只能提供抽象方法
举例
1 2 3 4 5 6 7 8 9 package charactor; public interface Mortal { public void die(); default public void revive() { System.out.println("本英雄复活了"); } }
为什么会有默认方法
假设没有默认方法这种机制,那么如果要为Mortal增加一个新的方法revive,那么所有实现了Mortal接口的类,都需要做改动。
但是引入了默认方法后,原来的类,不需要做任何改动,并且还能得到 这个默认方法
通过这种手段,就能够很好的扩展新的类,并且做到不影响原来的类
对象转型
引用类型与对象类型
在这个例子里,有一个对象 new ADHero(), 同时也有一个引用ad
对象是有类型的, 是ADHero 引用也是有类型的,是ADHero
通常情况下,引用类型和对象类型是一样的
1 2 3 4 5 6 7 8 9 10 11 12 package charactor; public class Hero { public String name; protected float hp; public static void main(String[] args) { ADHero ad = new ADHero(); } }
向上转型
子类转父类
一个很简单的判别办法
把右边的当做左边来用 ,看说得通不
所有的子类转换为父类 ,都是说得通的
举例
1 2 3 4 5 6 7 8 9 10 11 package charactor; public class Hero { public String name; protected float hp; public static void main(String[] args) { Hero h = new Hero();//h引用的类型是Hero ADHero ad = new ADHero();//ad引用的类型是ADHero h = ad; //把ADHero当做Hero使用,一定可以 } }
类转换接口
举例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 package charactor; public class Hero { public String name; protected float hp; public static void main(String[] args) { ADHero ad = new ADHero(); AD adi = ad; } }
从语义上来讲,把一个ADHero当做AD来使用,而AD接口只有一个physicAttack方法,这就意味着转换后就有可能要调用physicAttack方法,而ADHero一定是有physicAttack方法的,所以转换是能成功的。
向下转型
父类转子类
父类转子类,有的时候行,有的时候不行,所以必须进行强制转换。
强制转换的意思就是 转换有风险,风险自担。
接口转实现类
举例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 package charactor; public class Hero { public String name; protected float hp; public static void main(String[] args) { ADHero ad = new ADHero(); AD adi = ad; //ad引用指向ADHero, 而adi引用是接口类型:AD,实现类转换为接口,是向上转型,所以无需强制转换,并且一定能成功 ADHero adHero = (ADHero) adi; //adi实际上是指向一个ADHero的,所以能够转换成功 ADAPHero adapHero = (ADAPHero) adi; // adi引用所指向的对象是一个ADHero,要转换为ADAPHero就会失败。 adapHero.magicAttack(); } }
接口要先向下转型成为一个类,这个接口实际上已经只想一个类(应该不全,有空看看文章)
所以继承并实现这个接口的类才有可能让接口向下转型,与这个接口没有继承关系的类一定不能
instanceof
判断一个引用所指向的对象
举例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 package charactor; public class Hero { public String name; protected float hp; public static void main(String[] args) { ADHero ad = new ADHero(); APHero ap = new APHero(); Hero h1= ad; Hero h2= ap; //判断引用h1指向的对象,是否是ADHero类型 System.out.println(h1 instanceof ADHero); //判断引用h2指向的对象,是否是APHero类型 System.out.println(h2 instanceof APHero); //判断引用h1指向的对象,是否是Hero的子类型 System.out.println(h1 instanceof Hero); } }
多态
操作符的多态
如果+号两侧都是整型,那么+代表 数字相加
如果+号两侧,任意一个是字符串,那么+代表字符串连接
举例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 package charactor; public class Hero { public String name; protected float hp; public static void main(String[] args) { int i = 5; int j = 6; int k = i+j; //如果+号两侧都是整型,那么+代表 数字相加 System.out.println(k); int a = 5; String b = "5"; String c = a+b; //如果+号两侧,任意一个是字符串,那么+代表字符串连接 System.out.println(c); } }
类的多态
同一个类型,调用同一个方法,却能呈现不同的状态
关键代码
1 2 3 4 5 6 Item i1= new LifePotion(); // i1实际上指向LifePotion这种对象 Item i2 = new MagicPotion(); //i2实际上指向MagicPotion这种对象 System.out.print("i1 是Item类型,执行effect打印:"); //i1调用LifePotion对象中方法 i1.effect(); System.out.print("i2也是Item类型,执行effect打印:"); //i2调用MagicPotion对象中effect方法 i2.effect();
类的多态条件
父类(接口)引用指向子类对象
调用的方法有重写
使用多态与不使用多态区别
不使用多态
关键代码
1 2 3 4 5 6 public void useLifePotion(LifePotion lp){ lp.effect(); } public void useMagicPotion(MagicPotion mp){ mp.effect(); }
完整代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 package charactor; import property.LifePotion; import property.MagicPotion; public class Hero { public String name; protected float hp; public void useLifePotion(LifePotion lp){ lp.effect(); } public void useMagicPotion(MagicPotion mp){ mp.effect(); } public static void main(String[] args) { Hero garen = new Hero(); garen.name = "盖伦"; LifePotion lp =new LifePotion(); MagicPotion mp =new MagicPotion(); garen.useLifePotion(lp); garen.useMagicPotion(mp); } }
使用多态
关键代码
1 2 3 public void useItem(Item i){ i.effect(); }
完整代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 package charactor; import property.Item; import property.LifePotion; import property.MagicPotion; public class Hero { public String name; protected float hp; public void useItem(Item i){ i.effect(); } public static void main(String[] args) { Hero garen = new Hero(); garen.name = "盖伦"; LifePotion lp =new LifePotion(); MagicPotion mp =new MagicPotion(); garen.useItem(lp); garen.useItem(mp); } }
exercise
设计一个接口
接口叫做Mortal,其中有一个方法叫做die
实现接口
分别让ADHero,APHero,ADAPHero这三个类,实现Mortal接口,不同的类实现die方法的时候,都打印出不一样的字符串
为Hero类,添加一个方法,在这个方法中调用 m的die方法。
1 public void kill(Mortal m)
在主方法中 首先实例化出一个Hero对象:盖伦
然后实例化出3个对象,分别是ADHero,APHero,ADAPHero的实例 然后让盖伦 kill
这3个对象
answer
关键代码
1 2 3 4 5 6 7 8 9 10 public void kill(Mortal m) { m.die(); } ADHero shibing1 = new ADHero(); APHero shibing2 = new APHero(); ADAPHero shibing3 = new ADAPHero(); garen.kill(shibing1); garen.kill(shibing2); garen.kill(shibing3);
上面在尝试的时候发现只能在声明shibing的时候必须用对应的类或者声明成接口类型Mortal,如果声明成Hero类型会报错。个人理解原因是Hero与Mortal之间没有继承关系
完整代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 package charactor; import property.Item; import property.LifePotion; import property.MagicPotion; public class Hero { public String name; protected float hp; public void useItem(Item i){ i.effect(); } public void kill(Mortal m) { m.die(); } public static void main(String[] args) { Hero garen = new Hero(); garen.name = "盖伦"; Mortal shibing1 = new ADHero(); Mortal shibing2 = new APHero(); Mortal shibing3 = new ADAPHero(); garen.kill(shibing1); garen.kill(shibing2); garen.kill(shibing3); } }
隐藏
与重写类似,方法的重写是 子类覆盖父类的对象方法
隐藏 ,就是子类覆盖父类的类方法
子类中的静态方法覆盖率父类中的静态方法
举例:
父类
1 2 3 4 5 6 7 8 9 10 11 12 13 package charactor; public class Hero { public String name; protected float hp; //类方法,静态方法 //通过类就可以直接调用 public static void battleWin(){ System.out.println("hero battle win"); } }
子类
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 package charactor; public class ADHero extends Hero implements AD{ @Override public void physicAttack() { System.out.println("进行物理攻击"); } //隐藏父类的battleWin方法 public static void battleWin(){ System.out.println("ad hero battle win"); } public static void main(String[] args) { Hero.battleWin(); ADHero.battleWin(); } }
exercise:
Hero h =new ADHero();
h.battleWin(); //battleWin是一个类方法 h是父类类型的引用
但是指向一个子类对象 h.battleWin();
会调用父类的方法?还是子类的方法?
answer:
静态方法与类直接关联,而不是与实例对象关联 。所以声明为Hero的类,就调用Hero的类方法
super关键字
父类显式提供无参构造方法
关键代码
1 2 3 public Hero(){ System.out.println("Hero的构造方法 "); }
完整代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 package charactor; import property.Item; public class Hero { String name; //姓名 float hp; //血量 float armor; //护甲 int moveSpeed; //移动速度 public void useItem(Item i){ System.out.println("hero use item"); i.effect(); } public Hero(){ System.out.println("Hero的构造方法 "); } public static void main(String[] args) { new Hero(); } }
实例化子类,父类的构造方法一定会被调用
实例化一个ADHero(), 其构造方法会被调用
其父类的构造方法也会被调用
并且是父类构造方法先调用 子类构造方法会默认调用父类的
无参的构造方法
关键代码
1 2 3 4 5 6 7 public ADHero(){ System.out.println("AD Hero的构造方法"); } new ADHero();
完整代码 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 package charactor; public class ADHero extends Hero implements AD{ @Override public void physicAttack() { System.out.println("进行物理攻击"); } public ADHero(){ System.out.println("AD Hero的构造方法"); } public static void main(String[] args) { new ADHero(); } }
输出
父类显式提供两个构造方法
分别是无参的构造方法和带一个参数的构造方法
关键代码
1 2 3 4 5 6 7 8 public Hero(){ System.out.println("Hero的无参的构造方法 "); } public Hero(String name){ System.out.println("Hero的有一个参数的构造方法 "); this.name = name; }
完整代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 package charactor; import property.Item; public class Hero { String name; //姓名 float hp; //血量 float armor; //护甲 int moveSpeed; //移动速度 public void useItem(Item i){ System.out.println("hero use item"); i.effect(); } public Hero(){ System.out.println("Hero的无参的构造方法 "); } public Hero(String name){ System.out.println("Hero的有一个参数的构造方法 "); this.name = name; } public static void main(String[] args) { new Hero(); } }
子类显式调用父类带参构造方法
使用关键字super 显式调用父类带参的构造方法
关键代码
1 2 3 4 public ADHero(String name){ super(name); System.out.println("AD Hero的构造方法"); }
完整代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 package charactor; public class ADHero extends Hero implements AD{ @Override public void physicAttack() { System.out.println("进行物理攻击"); } public ADHero(String name){ super(name); System.out.println("AD Hero的构造方法"); } public static void main(String[] args) { new ADHero("德莱文"); } }
调用父类属性
Objext类
Object类是所有类的父类
toString()
toString()的意思是返回当前对象的字符串表达
举例说明
直接调用原式版本的toString
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 package charactor; public class Hero { public String name; protected float hp; // public String toString(){ // return name; // } public static void main(String[] args) { Hero h = new Hero(); h.name = "盖伦"; System.out.println(h.toString()); //直接打印对象就是打印该对象的toString()返回值 System.out.println(h); } }
重写toString
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 package charactor; public class Hero { public String name; protected float hp; @Override public String toString(){ return name; } public static void main(String[] args) { Hero h = new Hero(); h.name = "盖伦"; System.out.println(h.toString()); //直接打印对象就是打印该对象的toString()返回值 System.out.println(h); } }
finalize()
当一个对象没有任何引用指向的时候,它就满足垃圾回收的条件
当它被垃圾回收的时候,它的finalize() 方法就会被调用。
finalize() 不是开发人员主动调用的方法,而是由虚拟机JVM调用的。
举例
equals()
equals() 用于判断两个对象的内容是否相同
原始
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 package charactor; public class Hero { public String name; protected float hp; // public boolean equals(Object o){ // if(o instanceof Hero){ // Hero h = (Hero) o; // return this.hp == h.hp; // } // return false; // } public static void main(String[] args) { Hero h1= new Hero(); h1.hp = 300; Hero h2= new Hero(); h2.hp = 400; Hero h3= new Hero(); h3.hp = 300; System.out.println(h1.equals(h2)); System.out.println(h1.equals(h3)); } }
输出
改写:
假设,当两个英雄的hp相同的时候,我们就认为这两个英雄相同
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 package charactor; public class Hero { public String name; protected float hp; public boolean equals(Object o){ if(o instanceof Hero){ Hero h = (Hero) o; return this.hp == h.hp; } return false; } public static void main(String[] args) { Hero h1= new Hero(); h1.hp = 300; Hero h2= new Hero(); h2.hp = 400; Hero h3= new Hero(); h3.hp = 300; System.out.println(h1.equals(h2)); System.out.println(h1.equals(h3)); } }
==
这不是Object的方法,但是用于判断两个对象是否相同
更准确的讲 ,用于判断两个引用,是否指向了同一个对象
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 package charactor; public class Hero { public String name; protected float hp; public boolean equals(Object o){ if(o instanceof Hero){ Hero h = (Hero) o; return this.hp == h.hp; } return false; } public static void main(String[] args) { Hero h1= new Hero(); h1.hp = 300; Hero h2= new Hero(); h2.hp = 400; Hero h3= new Hero(); h3.hp = 300; System.out.println(h1==h2); System.out.println(h1==h3); } }
输出
final
final修饰类
当Hero被修饰成final的时候,表示Hero不能够被继承
其子类会出现编译错误
final修饰方法
父类中被final修饰的方法不能在子类中重写
final修饰基本类型变量
final修饰基本类型变量,表示该变量只有一次赋值机会
final修饰引用
final修饰引用
h引用被修饰成final,表示该引用只有1 次指向对象的机会
抽象类
定义
在类中声明一个方法,这个方法没有实现体,是一个“空”方法
这样的方法就叫抽象方法,使用修饰符“abstract"
当一个类有抽象方法的时候,该类必须被声明为抽象类
抽象类可以没有抽象方法,一旦一个类被声明为抽象类,就不能够被直接实例化
与子类的关系
为Hero增加一个抽象方法
attack ,并且把Hero声明为abstract的。继承Hero类后,这些子类就必须提供 不一样的attack方法实现。
抽象类和接口的区别
区别1:
子类只能继承一个抽象类,不能继承多个
子类可以实现多个 接口
区别2:
抽象类可以定义
public,protected,package,private、静态和非静态属性、final和非final属性
接口中声明的属性,即便没有显式的声明,也只能是public、静态和final
内部类
非静态内部类
定义:
非静态内部类可以直接在一个类里面定义
语法
new 外部类().new 内部类()
使用
作为Hero的非静态内部类,是可以直接访问外部类的private 实例属性name的
举例
关键代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 // 非静态内部类,只有一个外部类对象存在的时候,才有意义 // 战斗成绩只有在一个英雄对象存在的时候才有意义 class BattleScore { int kill; int die; int assit; public void legendary() { if (kill >= 8) System.out.println(name + "超神!"); else System.out.println(name + "尚未超神!"); } } // 实例化内部类 // BattleScore对象只有在一个英雄对象存在的时候才有意义 // 所以其实例化必须建立在一个外部类对象的基础之上 BattleScore score = garen.new BattleScore();
完整代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 package charactor; public class Hero { private String name; // 姓名 float hp; // 血量 float armor; // 护甲 int moveSpeed; // 移动速度 // 非静态内部类,只有一个外部类对象存在的时候,才有意义 // 战斗成绩只有在一个英雄对象存在的时候才有意义 class BattleScore { int kill; int die; int assit; public void legendary() { if (kill >= 8) System.out.println(name + "超神!"); else System.out.println(name + "尚未超神!"); } } public static void main(String[] args) { Hero garen = new Hero(); garen.name = "盖伦"; // 实例化内部类 // BattleScore对象只有在一个英雄对象存在的时候才有意义 // 所以其实例化必须建立在一个外部类对象的基础之上 BattleScore score = garen.new BattleScore(); score.kill = 9; score.legendary(); } }
静态内部类
定义
与非静态内部类不同,静态内部类 水晶类的实例化
不需要一个外部类的实例为基础 ,可以直接实例化
语法
new 外部类.静态内部类();
使用
因为没有一个外部类的实例,所以在静态内部类里面不可以访问外部类的实例属性和方法
除了可以访问外部类的私有静态成员外 ,静态内部类和普通类没什么大的区别
举例
关键代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 static class EnemyCrystal{ int hp=5000; //如果水晶的血量为0,则宣布胜利 public void checkIfVictory(){ if(hp==0){ Hero.battleWin(); //静态内部类不能直接访问外部类的对象属性 System.out.println(name + " win this game"); } } } //实例化静态内部类 Hero.EnemyCrystal crystal = new Hero.EnemyCrystal();
完整代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 package charactor; public class Hero { public String name; protected float hp; private static void battleWin(){ System.out.println("battle win"); } //敌方的水晶 static class EnemyCrystal{ int hp=5000; //如果水晶的血量为0,则宣布胜利 public void checkIfVictory(){ if(hp==0){ Hero.battleWin(); //静态内部类不能直接访问外部类的对象属性 System.out.println(name + " win this game"); } } } public static void main(String[] args) { //实例化静态内部类 Hero.EnemyCrystal crystal = new Hero.EnemyCrystal(); crystal.checkIfVictory(); } }
匿名类
定义
匿名类指的是在声明一个类的同时实例化它 ,使代码更加简洁精练
通常情况下,要使用一个接口或者抽象类,都必须创建一个子类
有的时候,为了快速使用,直接实例化一个抽象类,并“当场 ”实现其抽象方法。
既然实现了抽象方法,那么就是一个新的类,只是这个类,没有命名。
这样的类,叫做匿名类
在匿名类中使用外部的局部变量,外部的局部变量必须修饰为final
举例
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 package charactor; public abstract class Hero { String name; //姓名 float hp; //血量 float armor; //护甲 int moveSpeed; //移动速度 public abstract void attack(); public static void main(String[] args) { ADHero adh=new ADHero(); //通过打印adh,可以看到adh这个对象属于ADHero类 adh.attack(); System.out.println(adh); Hero h = new Hero(){ //当场实现attack方法 public void attack() { System.out.println("新的进攻手段"); } }; h.attack(); //通过打印h,可以看到h这个对象属于Hero$1这么一个系统自动分配的类名 System.out.println(h); } }
本地类
定义
本地类可以理解为有名字的匿名类 ,可以直接声明在代码块里面,可以是主方法,for循环里等等地方
举例
关键代码
1 2 3 4 5 6 //与匿名类的区别在于,本地类有了自定义的类名 class SomeHero extends Hero{ public void attack() { System.out.println( name+ " 新的进攻手段"); } }
完整代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 package charactor; public abstract class Hero { String name; //姓名 float hp; //血量 float armor; //护甲 int moveSpeed; //移动速度 public abstract void attack(); public static void main(String[] args) { //与匿名类的区别在于,本地类有了自定义的类名 class SomeHero extends Hero{ public void attack() { System.out.println( name+ " 新的进攻手段"); } } SomeHero h =new SomeHero(); h.name ="地卜师"; h.attack(); } }
UML图
类图
接口图
继承关系