1,繼承
(1),
關鍵字 extends
public class Employee {
}
public class Assistant extends Employee {
}
(2),在父子的繼承的關系當中,若果重命則創建子類對象的時候,有兩種方式
(a),直接通過子類對象訪問成員變量, 等號左邊是誰優先用誰
(b),間接通過成員方法訪問成員變量
public static void main(String[] args) {
Fu fu = new Fu();
System.out.println(fu.numFu);
Zi zi = new Zi();
System.out.println(zi.numFu);
System.out.println(zi.numZi);
System.out.println("===============");
System.out.println(zi.num); //優先子類 200
// System.out.println(zi.abc); //到處都沒有
System.out.println("===============");
//這個方法是子類,優先用子類的。沒有在向上找
zi.methodZi();//200
//這個方法是父類的
zi.methodFu();//100
(3),變量的使用
局部變量; 直接寫
本類的成員變量: this.成員變量
父類的成員變量: super.成員變量
public class Fu {
int num = 10;
}
public class Zi extends Fu{
int num = 20;
public void method(){
int num = 30;
System.out.println(num); //30
System.out.println(this.num); //20
System.out.println(super.num); //10
}
}
public class Demo01ExtendsFiled {
public static void main(String[] args) {
Zi zi = new Zi();
zi.method();
}
}
(4),在父子類的繼承關系當中,創建子類對象訪問成員方法的規則
注意事項:
無論是成員方法還是成員變量,如果沒有都是向上找父類,絕不會向下找子類
重寫 (OverWrite):方法名稱一樣,參數列表也一樣
重載 (Overload):方法名稱一樣,參數列表不一樣
public class Fu {
public void methodFu(){
System.out.println("父類方法執行!");
}
public void method(){
System.out.println("父類重名方法執行!");
}
}
public class Zi extends Fu {
public void methodZi(){
System.out.println("子類執行!");
}
public void method(){
System.out.println("子類重名方法執行!");
}
}
public class Demo01extendsMethod {
public static void main(String[] args) {
Zi zi = new Zi();
zi.methodZi();
zi.methodFu();
//創建的是new子類對象,所以優先用子類
zi.method();
}
}
(5),方法覆蓋重寫的注意事項
1,必須保證父子類之間的方法名稱相同,參數列表也相同,@Override 寫在方法前面,用來檢測是不是有效的重寫,這個注解不寫也是對的。
2,子類方法的返回值,必須[小于等于]父類方法得返回值得范圍,Object類是所有類的公共最高父類(祖宗類) java.lang.String 是object得子類
3,子類方法得權限必須大于等于父類權限修飾符,public > Protect > (default) > private,default 是什么都不寫,空著
public class Demo01OverWrite {
}
class Fu{
public Object method(){
return null;
}
}
class Zi extends Fu{
@Override
public Object method(){
return null;
}
}
(6),父子類構造方法得訪問特點
1,子類構造方法中有一個默認得"super()"調用,什么都沒寫,默認贈送。
2,可以通過super關鍵字來子類構造調用父類重載構造
3,super的父類構造調用,必須是子類構造方法得第一個語句。不能一個子類構造,調用多個父類構造
總結:
子類必須調用父類構造方法,不寫則贈送super,寫了則調用指定的super調用,super只能有一個,還必須是第一個
public class Demo01Constructor {
public static void main(String[] args) {
Zi zi = new Zi();
}
}
class Fu{
public Fu(){
}
public Fu(int num){
System.out.println("父類構造方法");
}
}
class Zi extends Fu{
// super();隱藏的
public Zi(){
super(10);
//super(16);
System.out.println("子類構造方法");
}
public void method(){
// super(10); 寫法錯誤!只有子類構造方法,才能調用父類構造方法。
}
}
(7),super 關鍵字的用法三種:
1,在子類的成員方法中,訪問父類的成員變量。
2,在子類的成員方法中,調用父類的成員方法。
3,在子類的構造方法中,訪問父類的構造方法。
(8),super 關鍵字用來訪問父類內容,this關鍵字用來訪問本類內容,用法三種
1,在本類的成員方法中,訪問本類成員變量
2,在本類的成員方法中,訪問本類的另一個成員方法
3,在本類的構造方法中,訪問苯類的另一個構造方法
this方法也必須是構造方法的第一個語句。
super 和 this 兩種構造不能同時使用。
public class Fu {
int num = 30;
}
public class Zi extends Fu {
int num = 20;
public Zi(){
this(123);//本類的無參構造,調用本類的有參構造。
}
public Zi(int num){
}
public Zi(int n, int m){
}
public void showNum(){
int num = 10;
System.out.println(num);//局部變量
System.out.println(this.num);//本類變量
System.out.println(super.num );//父類中的成員變量
}
public void methodA(){
System.out.println("AAA");
}
public void methodB(){
this.methodA();
System.out.println("BBB");
}
}
2,抽象類
(1),
1,不能直接創建抽象類對象。
2,必須用一個子類繼承父類。
3,子類必須覆蓋重寫抽象父類當中的所有抽象方法。覆蓋重寫(實現),子類去掉抽象方法的abstract關鍵字,然后補上方法大括號。
4,常見子類對象。
public abstract class Animal {
public abstract void eat();
public void method(){
}
}
public class Dog extends Animal {
@Override
public void eat() {
System.out.println("吃骨頭");
}
}
public class DemoMain {
public static void main(String[] args) {
Dog dog = new Dog();
dog.eat();
}
}
(2),
一個抽象方法不一定非得有抽象類,只要保證抽象方法在抽象類里就行.
public abstract class MyAbstract {
}
(3),抽象類的應用
//最高的抽像父類
public abstract class Animal {
public abstract void sleep();
public abstract void eat();
}
abstract class Dog extends Animal{
@Override
public void eat(){
System.out.println("狗吃骨頭");
}
public abstract void sleep();
}
class DogGolden extends Dog {
@Override
public void sleep(){
System.out.println("呼呼呼。。。。");
}
}
class Dog2H extends Dog{
@Override
public void sleep(){
System.out.println("嘿嘿嘿。。。");
}
}
class DemoMain{
public static void main(String[] args) {
DogGolden dogGolden = new DogGolden();
dogGolden.eat();
dogGolden.sleep();
}
}
(4),發紅包案例
import java.util.ArrayList;
import java.util.Random;
public class Member extends User {
public Member() {
}
public Member(String name, int money) {
super(name, money);
}
public void receive(ArrayList<Integer> list) {
//從多個紅包里抽取一個
//隨機獲取一個索引編號
int index = new Random().nextInt(list.size());
//從集合當中刪除,并得到被刪除的紅包給我自己
Integer date = list.remove(index);
//當前成員有多少錢
int money = super.getMoney();
//加上,并重新設置回去
super.setMoney(money + date);
}
}
import java.util.ArrayList;
import java.util.Random;
public class Member extends User {
public Member() {
}
public Member(String name, int money) {
super(name, money);
}
public void receive(ArrayList<Integer> list) {
//從多個紅包里抽取一個
//隨機獲取一個索引編號
int index = new Random().nextInt(list.size());
//從集合當中刪除,并得到被刪除的紅包給我自己
Integer date = list.remove(index);
//當前成員有多少錢
int money = super.getMoney();
//加上,并重新設置回去
super.setMoney(money + date);
}
}
import java.util.ArrayList;
public class Manager extends User {
public Manager(){
}
public Manager(String name, int money) {
super(name, money);
}
public ArrayList<Integer> send(int totalMoney, int count ) {
//需要一個集合,用來儲存若干個紅包的金額
ArrayList<Integer> redList = new ArrayList<>();
//看一下群主有多少錢
int leftMoney = super.getMoney();
if(totalMoney > leftMoney) {
System.out.println("余額不足!");
return redList;
}
//扣錢
super.setMoney(leftMoney - totalMoney);
//發紅包,分成多少分
int avg = totalMoney / count;
int mod = totalMoney % count;//余額,也就是甩下的零頭
//剩下的零頭,擺在最后一個紅包里
//把紅包放到集合里
for (int i = 0; i < count - 1; i++) {
redList.add(avg);
}
//最后一個紅包
int last = avg + mod;
redList.add(last);
return redList;
}
}
import java.util.ArrayList;
public class MainRedPacket {
public static void main(String[] args) {
Manager manager = new Manager("群主",100);
Member one = new Member("成員A",0);
Member two = new Member("成員B",0);
Member three = new Member("成員C",0);
manager.show();
one.show();
two.show();
three.show();
System.out.println("==================");
ArrayList<Integer> redList = manager.send(20,3);
one.receive(redList);
two.receive(redList);
three.receive(redList);
manager.show();
one.show();
two.show();
three.show();
}
}
3,接口
(1),
接口就是多個類的公共規范。
接口就是一種引用類型,最重要的內容,就是抽象方法
定義一個接口的方法
public interface 接口名稱 {
//接口的內容
}
備注:換成了關鍵字 interface之后,編譯器生成的字節碼文件仍然是:.java --> .class
java7 接口內的內容包括:
1,常量
2,抽象方法
如果是java8 還可以額外包含有
3,默認方法
4,靜態方法
java9
5,私有方法
接口使用步驟
1,接口不能直接使用,必須有一個實現類實現接口。
格式:
public class 實現類名稱 implements 接口名稱 {
//實現..........
}
2,接口的實現類必須覆蓋重寫接口所有的抽象類
3,創建實現類的對象,進行使用。
注意事項:
如果實現類并沒有覆蓋重寫接口中所有的抽象方法,那么這個實現類自己就必須是一個抽象類。
/*
抽象方法
public abstract 返回值類型 方法名稱(參數列表);
注意事項:
1,接口當中的抽象方法,修飾符必須是兩個固定的關鍵字, Public abstract
2,這兩個關鍵字的修飾符,可以選則性的省略。(今天新學不推薦)
*/
public interface MyInterFaceAbstract {
//抽象方法
public abstract void methodAbstract1();
void methodAbstract2(); //這也是抽象類
}
public class MyInterfaceAbstractImpl implements MyInterFaceAbstract {
@Override
public void methodAbstract1() {
System.out.println("這是第一個方法。");
}
@Override
public void methodAbstract2() {
System.out.println("這是第二個方法。");
}
}
public class Demo01InterFace {
public static void main(String[] args) {
//創建實現類使用
MyInterfaceAbstractImpl impl = new MyInterfaceAbstractImpl();
impl.methodAbstract1();
impl.methodAbstract2();
}
}
(2),默認方法
1,接口的默認方法,可以被接口的實現類對象,直接調用
2,接口的默認方法,可以被接口的實現類進行覆蓋重寫
package cn.itcast.day10.demo01;
/*
從Java8 開始,接口允許定義默認方法
格式:
public default 返回值類型 方法名稱 {
方法體
}
備注:
接口中的默認方法,可以解決接口升級的問題
*/
public interface MyInterfaceDefault {
//抽象方法
public abstract void methodAbs ();
//新添加了一個抽象方法
// public abstract void methodAbs2 ();
public default void methodDefault () {
System.out.println("這是新添加的默認方法");
}
}
package cn.itcast.day10.demo01;
public class MyInterfaceDefaultA implements MyInterfaceDefault{
@Override
public void methodAbs() {
System.out.println("實現了方法AAA");
}
}
package cn.itcast.day10.demo01;
public class MyInterfaceDefaultB implements MyInterfaceDefault{
@Override
public void methodAbs() {
System.out.println("實現了方法BBB");
}
@Override
public void methodDefault() {
System.out.println("實現了類B的覆蓋重寫");
}
}
public class Demo02IInterfaceDefault {
public static void main(String[] args) {
MyInterfaceDefaultA a = new MyInterfaceDefaultA();
a.methodAbs();//調用抽象方法,實施運行的是右側實現類
//調用默認方法,如果實現類中沒有,會向上找接口
a.methodDefault();
MyInterfaceDefaultB b = new MyInterfaceDefaultB();
b.methodAbs();
b.methodDefault();
}
}
(3),靜態方法
不能通過接口實現類的對象來調用接口中的靜態問題。
正確用法:通過接口名稱直接調用其中的靜態方法。
/*
Java8 開始,接口中準許定義靜態方法
格式:
public static 返回值類型 方法名稱(參數列表) {
方法體;
}
*/
public interface MyInterfaceStatic {
public static void methodStatic(){
System.out.println("這是接口的靜態方法!");
}
}
package cn.itcast.day10.demo01;
public class MyInterfaceStaticImpl implements MyInterfaceStatic{
}
public class Demo03InterfaceStatic {
public static void main(String[] args) {
// MyInterfaceStaticImpl impl = new MyInterfaceStaticImpl();
//錯誤寫法!
// impl.methodStatic;
//調用靜態方法
MyInterfaceStatic.methodStatic();
}
}
(4), 私有方法
問題描述:
我們需要抽取出來一個公共方法,用來解決默認方法之間的重復代碼問題
但是這個公共方法不應該上實現類使用,私有化
定義私有方法:
1,普通私有方法。解決默認方法之間的代碼重復問題。
2,靜態私有方法。解決靜態方法之間的代碼重復問題。
public interface MyInterfacePrivateA {
public default void methodPrivate1() {
System.out.println("默認方法1!");
methodCommon();
}
public default void methodPrivate2() {
System.out.println("默認方法2!");
methodCommon();
}
private void methodCommon() {
System.out.println("AAA");
System.out.println("BBB");
System.out.println("CCC");
}
}
public class MyInterfacePrivateAImpl implements MyInterfacePrivateA {
public void methodAnother() {
System.out.println("實現類方法!");
}
}
public class Demo04InterfacePrivate {
public static void main(String[] args) {
MyInterfacePrivateAImpl impl = new MyInterfacePrivateAImpl();
impl.methodPrivate1();
impl.methodPrivate2();
impl.methodAnother();
}
}
(6),常量
final 關鍵子的作用
如果引用為基本數據類型,則該引用為常量,該值無法修改;
如果引用為引用數據類型,比如對象、數組,則該對象、數組本身可以修改,但指向該對象或數組的地址的引用不能修改。
如果引用時類的成員變量,則必須當場賦值,否則編譯會報錯。
接口中可以定義“成員變量”,這個成員變量必須使用public static final 三個關鍵字來修飾。
從效果上看就是接口的【常量】。
public static final 數據類型 常量量名稱 = 數據值;
注意事項:
1,接口中的常量可以省略 public static final ,注意,不寫照樣是這一樣
2,接口譜中的的常量必須賦值,不能不賦值。
3,接口中的常量的名稱全用大寫,并且用下劃線分割。
public interface MyInterfaceConst {
//這就是一個常量,一旦賦值,不可修改
public static final int NUM_Of_CLASS = 10;
}
public class Demo05InterfaceConst {
public static void main(String[] args) {
//訪問接口中的常量
System.out.println(MyInterfaceConst.NUM_Of_CLASS);
}
}
(7),接口的使用
使用接口的時候需要注意
1,接口沒有靜態代碼塊,或者構造方法的。
2,一個類的直接父類只有一個,但是一個類可以同時實現多個接口
格式;
public class MyInterfaceImpl implements MyInterfaceA, MyInterfaceB{
//覆蓋重寫所用抽象方法
}
3,如果實現類所實現的多個接口當中,存在重復的抽象方法,那麼只需要覆蓋重寫一次即可
4,如果實現類沒有完全覆蓋重寫所有接口的的抽象方法,那么這個類必須是抽象類。
5,如果實現類所實現的多個接口當中,存在重復的默認方法,那么實現類必須對沖突的默認方法覆蓋重寫。
6.一類若果直接父類當中的方法,和接口當中的默認方法產生了沖突,優先用父類當中的方法
package cn.itcast.day10.demo02;
public interface MyInterface {
public default void method() {
System.out.println("接口的默認方法!");
}
}
public class Fu {
public void method(){
System.out.println("父類方法!");
}
}
package cn.itcast.day10.demo02;
public class Zi extends Fu implements MyInterface {
}
public interface MyInterfaceA {
//錯誤寫法 接口不能有靜態代碼塊
// static {
// }
//錯誤寫法,接口不能有構造方法
// public MyInterface(){
// }
public abstract void methodA();
public abstract void methodAbs();
public default void methodDefault(){
System.out.println("默認方法AAAAA");
}
}
public interface MyInterfaceB {
//錯誤寫法 接口不能有靜態代碼塊
// static {
// }
//錯誤寫法,接口不能有構造方法
// public MyInterface(){
// }
public abstract void methodB();
public abstract void methodAbs();
public default void methodDefault(){
System.out.println("默認方法BBBBB");
}
}
public class MyInterfaceImpl /*extends Object*/ implements MyInterfaceA, MyInterfaceB {
@Override
public void methodA() {
System.out.println("覆蓋重寫了A!");
}
@Override
public void methodB() {
System.out.println("覆蓋重寫了B!");
}
@Override
public void methodAbs() {
System.out.println("覆蓋重寫了AB接口都有的抽象方法!");
}
@Override
public void methodDefault() {
System.out.println("對多個接口沖突的默認方法驚醒覆蓋重寫");
}
}
public abstract class MyInterfaceAbstract implements MyInterfaceA, MyInterfaceB{
@Override
public void methodA() {
}
@Override
public void methodAbs() {
}
@Override
public void methodDefault(){
System.out.println();
}
}
public class Demo01Interface {
public static void main(String[] args) {
MyInterfaceImpl impl = new MyInterfaceImpl();
impl.methodDefault();
Zi zi = new Zi();
zi.method();
}
}
(8),
類與類之間是單繼承的,直接父類只能有一個。
類與接口之間是對實現的。一個類可以實現多個接口。
接口與接口之間是多繼承的,
public interface MyInterfaceA {
public abstract void methodA();
public abstract void methodCommon();
}
public interface MyInterfaceB {
public abstract void methodB();
public abstract void methodCommon();
}
/*
這個子接口中有幾個方法?
4個 methodA 來源于接口A methodB 來源于接口B methodCommon 來源于接口A和B
*/
public interface MyInterface extends MyInterfaceA, MyInterfaceB {
public abstract void method();
}
public class Demo01Relations {
public static void main(String[] args) {
}
}
4,多態
(1),定義
代碼當中體現多態性,其實就是一句話:父類引用指向子類對象。
格式:
父類名稱 對象名 = new 子類名稱();
或者:
接口名稱 對象名 = new 實現類名稱();
public class Demo01Multi {
public static void main(String[] args) {
// 使用多態的寫法
// 左側父類的引用,指向了右側子類的對象
Fu obj = new Zi();
obj.method();
obj.methodFu();
}
}
public class Fu {
public void method() {
System.out.println("父類方法");
}
public void methodFu() {
System.out.println("父類特有方法");
}
}
public class Zi extends Fu{
@Override
public void method() {
System.out.println("子類方法");
}
}
(2),多態的使用
訪問成員變量的兩種方式:
- 直接通過對象名稱訪問成員變量:看等號左邊是誰,優先用誰,沒有則向上找。
- 間接通過成員方法訪問成員變量:看該方法屬于誰,優先用誰,沒有則向上找。
public class DEmo01MultiField {
public static void main(String[] args) {
// 使用多態的寫法,父類引用指向子類對象
Fu obj = new Zi();
System.out.println(obj.num); // 父:10
// System.out.println(obj.age); // 錯誤寫法!
System.out.println("=============");
// 子類沒有覆蓋重寫,就是父:10
// 子類如果覆蓋重寫,就是子:20
obj.showNum();
}
}
public class Fu {
int num = 10;
public void showNum() {
System.out.println(num);
}
public void method() {
System.out.println("父類方法");
}
public void methodFu() {
System.out.println("父類特有方法");
}
}
public class Zi extends Fu {
int num = 20;
int age = 16;
@Override
public void showNum() {
System.out.println(num);
}
@Override
public void method() {
System.out.println("子類方法");
}
public void methodZi() {
System.out.println("子類特有方法");
}
}
在多態的代碼當中,成員方法的訪問規則是:
看new的是誰,就優先用誰,沒有則向上找。
口訣:編譯看左邊,運行看右邊。
對比一下:
成員變量:編譯看左邊,運行還看左邊。
成員方法:編譯看左邊,運行看右邊。
public class Demo02MultiMethod {
public static void main(String[] args) {
Fu obj = new Zi(); // 多態
obj.method(); // 父子都有,優先用子
obj.methodFu(); // 子類沒有,父類有,向上找到父類
// 編譯看左邊,左邊是Fu,Fu當中沒有methodZi方法,所以編譯報錯。
// obj.methodZi(); // 錯誤寫法!
}
}
(3),多態的使用
向上轉型一定是安全的,沒有問題的,正確的。但是也有一個弊端:
對象一旦向上轉型為父類,那么就無法調用子類原本特有的內容。
解決方案:用對象的向下轉型【還原】。
如何才能知道一個父類引用的對象,本來是什么子類?
格式:
對象 instanceof 類名稱
這將會得到一個boolean值結果,也就是判斷前面的對象能不能當做后面類型的實例。
public abstract class Animal {
public abstract void eat();
}
public class Cat extends Animal{
@Override
public void eat() {
System.out.println("貓吃魚");
}
// 子類特有方法
public void catchMouse() {
System.out.println("貓抓老鼠");
}
}
public class Dog extends Animal {
@Override
public void eat() {
System.out.println("狗吃SHIT");
}
public void watchHouse() {
System.out.println("狗看家");
}
}
public class Demo01MAin {
public static void main(String[] args) {
// 對象的向上轉型,就是:父類引用指向之類對象。
Animal animal = new Cat(); // 本來創建的時候是一只貓
animal.eat(); // 貓吃魚
// animal.catchMouse(); // 錯誤寫法!
// 向下轉型,進行“還原”動作
Cat cat = (Cat) animal;
cat.catchMouse(); // 貓抓老鼠
// 下面是錯誤的向下轉型
// 本來new的時候是一只貓,現在非要當做狗
// 錯誤寫法!編譯不會報錯,但是運行會出現異常:
// java.lang.ClassCastException,類轉換異常
Dog dog = (Dog) animal;
}
}
public class Demo02Instanceof {
public static void main(String[] args) {
Animal animal = new Dog(); // 本來是一只狗
animal.eat(); // 狗吃SHIT
// 如果希望掉用子類特有方法,需要向下轉型
// 判斷一下父類引用animal本來是不是Dog
if (animal instanceof Dog) {
Dog dog = (Dog) animal;
dog.watchHouse();
}
// 判斷一下animal本來是不是Cat
if (animal instanceof Cat) {
Cat cat = (Cat) animal;
cat.catchMouse();
}
giveMeAPet(new Dog());
}
public static void giveMeAPet(Animal animal) {
if (animal instanceof Dog) {
Dog dog = (Dog) animal;
dog.watchHouse();
}
if (animal instanceof Cat) {
Cat cat = (Cat) animal;
cat.catchMouse();
}
}
}
(4),多態的練習
1,進行描述筆記本類,實現筆記本使用USB鼠標、USB鍵盤
2,USB接口,包含開啟功能、關閉功能 筆記本類,包含運行功能、關機功能、使用3,USB設備功能 鼠標類,要實現USB接口,并具備點擊的方法 鍵盤類,要實現4USB接口,具備敲擊的方法
public class Computer {
public void powerOn() {
System.out.println("筆記本電腦開機");
}
public void powerOff() {
System.out.println("筆記本電腦關機");
}
// 使用USB設備的方法,使用接口作為方法的參數
public void useDevice(USB usb) {
usb.open(); // 打開設備
if (usb instanceof Mouse) { // 一定要先判斷
Mouse mouse = (Mouse) usb; // 向下轉型
mouse.click();
} else if (usb instanceof Keyboard) { // 先判斷
Keyboard keyboard = (Keyboard) usb; // 向下轉型
keyboard.type();
}
usb.close(); // 關閉設備
}
}
public interface USB {
public abstract void open(); // 打開設備
public abstract void close(); // 關閉設備
}
public class Keyboard implements USB{
@Override
public void open() {
System.out.println("打開鍵盤");
}
@Override
public void close() {
System.out.println("關閉鍵盤");
}
public void type() {
System.out.println("鍵盤輸入");
}
}
public class Mouse implements USB {
@Override
public void open() {
System.out.println("打開鼠標");
}
@Override
public void close() {
System.out.println("關閉鼠標");
}
public void click() {
System.out.println("鼠標點擊");
}
}
public class DemoMain {
public static void main(String[] args) {
// 首先創建一個筆記本電腦
Computer computer = new Computer();
computer.powerOn();
// 準備一個鼠標,供電腦使用
// Mouse mouse = new Mouse();
// 首先進行向上轉型
USB usbMouse = new Mouse(); // 多態寫法
// 參數是USB類型,我正好傳遞進去的就是USB鼠標
computer.useDevice(usbMouse);
// 創建一個USB鍵盤
Keyboard keyboard = new Keyboard(); // 沒有使用多態寫法
// 方法參數是USB類型,傳遞進去的是實現類對象
computer.useDevice(keyboard); // 正確寫法!也發生了向上轉型
// 使用子類對象,匿名對象,也可以
// computer.useDevice(new Keyboard()); // 也是正確寫法
computer.powerOff();
System.out.println("==================");
method(10.0); // 正確寫法,double --> double
method(20); // 正確寫法,int --> double
int a = 30;
method(a); // 正確寫法,int --> double
}
public static void method(double num) {
System.out.println(num);
}
}
5,final關鍵字
(1),final關鍵字代表最終、不可改變的。
常見四種用法:
- 可以用來修飾一個類
- 可以用來修飾一個方法
- 還可以用來修飾一個局部變量
- 還可以用來修飾一個成員變量
final修飾一個類:
當final關鍵字用來修飾一個類的時候,格式:
public final class 類名稱 {
// ...
}
含義:當前這個類不能有任何的子類。(太監類)
注意:一個類如果是final的,那么其中所有的成員方法都無法進行覆蓋重寫(因為沒兒子。)
public final class MyClass /*extends Object*/ {
public void method() {
System.out.println("方法執行!");
}
}
// 不能使用一個final類來作為父類
public class MySubClass /*extends MyClass*/ {
}
final修飾一個方法:
當final關鍵字用來修飾一個方法的時候,這個方法就是最終方法,也就是不能被覆蓋重寫。
格式:
修飾符 final 返回值類型 方法名稱(參數列表) {
// 方法體
}
注意事項:
對于類、方法來說,abstract關鍵字和final關鍵字不能同時使用,因為矛盾。
public abstract class Fu {
public final void method() {
System.out.println("父類方法執行!");
}
public abstract /*final*/ void methodAbs() ;
}
public class Zi extends Fu {
@Override
public void methodAbs() {
}
// 錯誤寫法!不能覆蓋重寫父類當中final的方法
// @Override
// public void method() {
// System.out.println("子類覆蓋重寫父類的方法!");
// }
}
final修飾局部變量和成員變量
對于成員變量來說,如果使用final關鍵字修飾,那么這個變量也照樣是不可變。
- 由于成員變量具有默認值,所以用了final之后必須手動賦值,不會再給默認值了。
- 對于final的成員變量,要么使用直接賦值,要么通過構造方法賦值。二者選其一。
- 必須保證類當中所有重載的構造方法,都最終會對final的成員變量進行賦值。
public class Person {
private final String name/* = "鹿晗"*/;
public Person() {
name = "關曉彤";
}
public Person(String name) {
this.name = name;
}
public String getName() {
return name;
}
// public void setName(String name) {
// this.name = name;
// }
}
public class Student {
private String name;
public Student() {
}
public Student(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
6,權限修飾符 day11-code demo02
Java中有四種權限修飾符:
public > protected > (default) > private
同一個類(我自己) YES YES YES YES
同一個包(我鄰居) YES YES YES NO
不同包子類(我兒子) YES YES NO NO
不同包非子類(陌生人) YES NO NO NO
注意事項:(default)并不是關鍵字“default”,而是根本不寫。
7,內部類
(1),內部類的定義。
如果一個事物的內部包含另一個事物,那么這就是一個類內部包含另一個類。
例如:身體和心臟的關系。又如:汽車和發動機的關系。
分類:
- 成員內部類
- 局部內部類(包含匿名內部類)
成員內部類的定義格式:
修飾符 class 外部類名稱 {
修飾符 class 內部類名稱 {
// ...
}
// ...
}
注意:內用外,隨意訪問;外用內,需要內部類對象。
==========================
如何使用成員內部類?有兩種方式:
- 間接方式:在外部類的方法當中,使用內部類;然后main只是調用外部類的方法。
- 直接方式,公式:
類名稱 對象名 = new 類名稱();
【外部類名稱.內部類名稱 對象名 = new 外部類名稱().new 內部類名稱();】
類的定義:
public class Body { // 外部類
public class Heart { // 成員內部類
// 內部類的方法
public void beat() {
System.out.println("心臟跳動:蹦蹦蹦!");
System.out.println("我叫:" + name); // 正確寫法!
}
}
// 外部類的成員變量
private String name;
// 外部類的方法
public void methodBody() {
System.out.println("外部類的方法");
new Heart().beat();
}
測試類
public class Demo01InnerClass {
public static void main(String[] args) {
Body body = new Body(); // 外部類的對象
// 通過外部類的對象,調用外部類的方法,里面間接在使用內部類Heart
body.methodBody();
System.out.println("=====================");
// 按照公式寫:
Body.Heart heart = new Body().new Heart();
heart.beat();
}
}
重名時
public class Outer {
int num = 10; // 外部類的成員變量
public class Inner /*extends Object*/ {
int num = 20; // 內部類的成員變量
public void methodInner() {
int num = 30; // 內部類方法的局部變量
System.out.println(num); // 局部變量,就近原則
System.out.println(this.num); // 內部類的成員變量
System.out.println(Outer.this.num); // 外部類的成員變量
}
}
}
public class Demo02InnerClass {
public static void main(String[] args) {
// 外部類名稱.內部類名稱 對象名 = new 外部類名稱().new 內部類名稱();
Outer.Inner obj = new Outer().new Inner();
obj.methodInner();
}
}
(2),局部內部類
如果一個類是定義在一個方法內部的,那么這就是一個局部內部類。
“局部”:只有當前所屬的方法才能使用它,出了這個方法外面就不能用了。
定義格式:
修飾符 class 外部類名稱 {
修飾符 返回值類型 外部類方法名稱(參數列表) {
class 局部內部類名稱 {
// ...
}
}
}
小節一下類的權限修飾符:
public > protected > (default) > private
定義一個類的時候,權限修飾符規則:
1. 外部類:public / (default)
2. 成員內部類:public / protected / (default) / private
3. 局部內部類:什么都不能寫
class Outer {
public void methodOuter() {
class Inner { // 局部內部類
int num = 10;
public void methodInner() {
System.out.println(num); // 10
}
}
Inner inner = new Inner();
inner.methodInner();
}
}
局部內部類,如果希望訪問所在方法的局部變量,那么這個局部變量必須是【有效final的】
備注:從Java 8+開始,只要局部變量事實不變,那么final關鍵字可以省略。
原因:
- new出來的對象在堆內存當中。
- 局部變量是跟著方法走的,在棧內存當中。
- 方法運行結束之后,立刻出棧,局部變量就會立刻消失。
- 但是new出來的對象會在堆當中持續存在,直到垃圾回收消失。
public class MyOuter {
public void methodOuter() {
/**final*/ int num = 10; // 所在方法的局部變量
class MyInner {
public void methodInner() {
System.out.println(num);
}
}
}
}
(3),匿名內部類
如果接口的實現類(或者是父類的子類)只需要使用唯一的一次,
那么這種情況下就可以省略掉該類的定義,而改為使用【匿名內部類】。
匿名內部類的定義格式:
接口名稱 對象名 = new 接口名稱() {
// 覆蓋重寫所有抽象方法
};
對格式“new 接口名稱() {...}”進行解析:
- new代表創建對象的動作
- 接口名稱就是匿名內部類需要實現哪個接口
- {...}這才是匿名內部類的內容
另外還要注意幾點問題:
- 匿名內部類,在【創建對象】的時候,只能使用唯一一次。
如果希望多次創建對象,而且類的內容一樣的話,那么就需要使用單獨定義的實現類了。 - 匿名對象,在【調用方法】的時候,只能調用唯一一次。
如果希望同一個對象,調用多次方法,那么必須給對象起個名字。 - 匿名內部類是省略了【實現類/子類名稱】,但是匿名對象是省略了【對象名稱】
強調:匿名內部類和匿名對象不是一回事!!!
public interface MyInterface {
public abstract void method1(); // 抽象方法
void method2();
}
public class MyInterfaceImpl implements MyInterface {
@Override
public void method1() {
System.out.println("實現類覆蓋重寫了方法!111");
}
@Override
public void method2() {
System.out.println("實現類覆蓋重寫了方法!222");
}
}
測試類:
public class DemoMain {
public static void main(String[] args) {
// MyInterface obj = new MyInterfaceImpl();
// obj.method();
// MyInterface some = new MyInterface(); // 錯誤寫法!
// 使用匿名內部類,但不是匿名對象,對象名稱就叫objA
MyInterface objA = new MyInterface() {
@Override
public void method1() {
System.out.println("匿名內部類實現了方法!111-A");
}
@Override
public void method2() {
System.out.println("匿名內部類實現了方法!222-A");
}
};
objA.method1();
objA.method2();
System.out.println("=================");
// 使用了匿名內部類,而且省略了對象名稱,也是匿名對象
new MyInterface() {
@Override
public void method1() {
System.out.println("匿名內部類實現了方法!111-B");
}
@Override
public void method2() {
System.out.println("匿名內部類實現了方法!222-B");
}
}.method1();
// 因為匿名對象無法調用第二次方法,所以需要再創建一個匿名內部類的匿名對象
new MyInterface() {
@Override
public void method1() {
System.out.println("匿名內部類實現了方法!111-B");
}
@Override
public void method2() {
System.out.println("匿名內部類實現了方法!222-B");
}
}.method2();
}
}
(4),類作為成員變量類型
public class Weapon {
private String code; // 武器的代號
public Weapon() {
}
public Weapon(String code) {
this.code = code;
}
public String getCode() {
return code;
}
public void setCode(String code) {
this.code = code;
}
}
// 游戲當中的英雄角色類
public class Hero {
private String name; // 英雄的名字
private int age; // 英雄的年齡
private Weapon weapon; // 英雄的武器
public Hero() {
}
public Hero(String name, int age, Weapon weapon) {
this.name = name;
this.age = age;
this.weapon = weapon;
}
public void attack() {
System.out.println("年齡為" + age + "的" + name + "用" + weapon.getCode() + "攻擊敵方。");
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public Weapon getWeapon() {
return weapon;
}
public void setWeapon(Weapon weapon) {
this.weapon = weapon;
}
}
public class DemoMain {
public static void main(String[] args) {
// 創建一個英雄角色
Hero hero = new Hero();
// 為英雄起一個名字,并且設置年齡
hero.setName("蓋倫");
hero.setAge(20);
// 創建一個武器對象
Weapon weapon = new Weapon("AK-47");
// 為英雄配備武器
hero.setWeapon(weapon);
// 年齡為20的蓋倫用多蘭劍攻擊敵方。
hero.attack();
}
}
(5),接口作為成員變量的類型
定義一個接口:
public interface Skill {
public abstract void use(); // 釋放技能的抽象方法
//public abstract 可省略 一定是個抽象方法
}
接口的實現類:
public class SkillImpl implements Skill {
@Override
public void use() {
System.out.println("Biu~biu~biu~");
}
}
人物類:
public class Hero {
private String name; // 英雄的名稱
private Skill skill; // 英雄的技能
public Hero() {
}
public Hero(String name, Skill skill) {
this.name = name;
this.skill = skill;
}
public void attack() {
System.out.println("我叫" + name + ",開始施放技能:");
skill.use(); // 調用接口中的抽象方法
System.out.println("施放技能完成。");
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Skill getSkill() {
return skill;
}
public void setSkill(Skill skill) {
this.skill = skill;
}
}
測試類:
public class DemoGame {
public static void main(String[] args) {
Hero hero = new Hero();
hero.setName("艾希"); // 設置英雄的名稱
// 設置英雄技能
// hero.setSkill(new SkillImpl()); // 使用單獨定義的實現類
// 還可以改成使用匿名內部類
// Skill skill = new Skill() {
// @Override
// public void use() {
// System.out.println("Pia~pia~pia~");
// }
// };
// hero.setSkill(skill);
// 進一步簡化,同時使用匿名內部類和匿名對象
hero.setSkill(new Skill() {
@Override
public void use() {
System.out.println("Biu~Pia~Biu~Pia~");
}
});
hero.attack();
}
}
(6),接口作為參數和返回值類型
import java.util.ArrayList;
import java.util.List;
/*
java.util.List正是ArrayList所實現的接口。
*/
public class DemoInterface {
public static void main(String[] args) {
// 左邊是接口名稱,右邊是實現類名稱,這就是多態寫法
List<String> list = new ArrayList<>();
List<String> result = addNames(list);
for (int i = 0; i < result.size(); i++) {
System.out.println(result.get(i));
}
}
public static List<String> addNames(List<String> list) {
list.add("迪麗熱巴");
list.add("古力娜扎");
list.add("瑪爾扎哈");
list.add("沙揚娜拉");
return list;
}
}