目錄
- 1、類變數(靜態類變數)
- 2、靜態方法
- 3、Main()方法
- 4、代碼塊
- 5、單例模式
- 6、抽象類
- 6.1、抽象類的介紹
- 6.2、抽象類的特點
- 6.3、多型在抽象類的體現
- 6.4、抽象類體現了模板設計模式
- 7、介面
- 7.2、繼承與介面在應用上有什么區別?
- 7.3、介面的多型性
- 8、內部類
- 8.1 、內部類介紹
- 8.2、區域內部類
- 8.3、匿名內部類
- 8.4、 成員內部類
- 8.5、 靜態內部類
- 9、列舉
- 10、注解
- 10.1、Override
- 10.2、Deprecated
- 10.3、supresswarning
- 11、包裝類
1、類變數(靜態類變數)
1、為什么要有靜態類變數?
引出:
過年,公司發過年禮品,假設我們有一個禮品類Gift,有一個發放禮品的類方法receive(),我想知道當前有多少個員工領取禮品,怎么辦?
解決:
1、在類中定義一個屬性count,然后在receive()里每呼叫該方法進行conut++?
2、在main方法定義一個count區域變數,每呼叫一次receive()進行count++?
3、在類中定義一個靜態的類變數,在方法receive()中進行count++?
答案:選擇3!第一種不管領取了多少禮品,每個物件中的count屬性值都是1;第二種可以是可以,但不符合oop的思想,如果需要在別的地方用到該count值,沒辦法呼叫啊;第三種,靜態類變數多個物件共享該變數,不管是用那個物件獲取的count值,都是最新的且一致的!
#測驗代碼:
public class StaticDemo {
public static void main(String[] args) {
Gift gift = new Gift("張三");
Gift gift1 = new Gift("李四");
gift.receive();
System.out.println("當前有"+Gift.count+"個員工領取了禮品"+" 名字分別為:"+ Gift.str);
gift1.receive();
System.out.println("當前有"+gift1.count+"個員工領取了禮品"+" 名字分別為:"+ Gift.str);
}
}
//禮品類
class Gift{
private String name;
protected static int count;
public static String str="";
public Gift(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
//發放禮品
public void receive(){
giftEliminate();
count++;
str = str.concat(getName());
}
//當領取人數大于5個我就呼叫該方法將count清零;
public void giftEliminate(){
if (count>5){
Gift.count = 0;
}
}
}
輸出:
當前有1個員工領取了禮品 名字分別為:張三
當前有2個員工領取了禮品 名字分別為:張三李四
小結(什么時候使用類變數?):
1、靜態類變數的作用就是存盤一些獨立于物件的資料,但各物件又能共享該變數值
2、如何定義和使用靜態類變數?
定義:
1、static public int count
2、public static int count (推薦使用)
#從上面代碼可以看出,在普通屬性上宣告一個static關鍵字,靜態類方法也是如此
使用:
1、使用類名.變數名(推薦使用) Gift.count
2、物件名.變數名 gift1.count
3、類變數于實體變數(普通類屬性)的區別?
1、類變數屬于類,但被所有物件共享
2、實體變數只屬于這一個物件,獨立不被共享
4 小結:
類變數:
1、使用關鍵字static宣告定義,推薦定義格式: 修飾符 static 資料型別 變數名
2、使用類名.變數名呼叫該類變數
3、被所有物件共享變數值
實體變數:
1、定義:修飾符 資料型別 變數名
2、使用物件名.變數名呼叫該變數
3、只屬于該物件本身,不被共享
2、靜態方法
1、為什么要有靜態方法?
應用:
當我們要寫一個方法操作一些跟物件無關的資料時,可以使用靜態方法,
2、靜態方法的定義和使用
定義:
1、public static void print(){}
2、static void print(){}
修飾符 static 回傳資料型別 方法名(){}
#注意:因為修飾方法的修飾符只有public 或 默認(不寫)
使用:
1、類名.方法名() (推薦使用)
2、物件名.方法名()
代碼:
public class StaticDemo {
public static void main(String[] args) {
Student student = new Student("張三");
Student student1 = new Student("李四");
Student.count_study_money(3000);
Student.count_study_money(5000);
System.out.println("當前學費共交了"+Student.get_study_money()+"元");
student.print();
}
}
class Student{
public String name;
private static double study_money;
static String str="";
public Student(String name){
super();
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
//計算學費
public static void count_study_money(double study_money){
//Student.study_money = Student.study_money + study_money;
Student.study_money += study_money;
}
//回傳學費
public static double get_study_money(){
return Student.study_money;
}
public void print(){
System.out.println(get_study_money());//非靜態方法中呼叫靜態方法
System.out.println(name);//非靜態方法中呼叫非靜態變數
System.out.println(str);//非靜態方法中呼叫靜態變數
}
}
輸出:
當前學費共交了8000.0元
8000.0
張三
3、使用靜態方法時,有哪些要注意的細節?
1、類方法中不能使用this和super與物件相關的關鍵字, 因為this代表呼叫者當前物件本身,而靜態方法執行是無需實體化物件,那這個this就是空,所以靜態方法中不能用this
2、類方法和類變數呼叫跟物件是否創建無關,呼叫時都是通過類名.類變數/類方法()呼叫
3、而物件變數和物件方法與實體物件有關,呼叫物件方法和物件變數時只能用物件名.變數 或 物件名.方法名(),不能使用類名.方法和變數!
4、靜態(類)方法中只能呼叫靜態(類)變數和靜態方法,無法使用非靜態變數和非靜態方法
5、非靜態方法可以呼叫非靜態變數、非靜態方法、靜態方法、靜態變數
i
3、Main()方法
public static void main(String[] args){}
1、為什么main()方法要這樣寫?
# 因為有了public權限,所以jvm才能呼叫main;
因為有了static,所以呼叫mian()方法時不需要有物件創建;
因為有了String[] args字串陣列,所以運行main里面的程式時,可以用字串陣列去存盤中轉所需的引數!
4、代碼塊
1、代碼塊的定義
1、普通代碼塊
格式:{代碼體}
2、靜態代碼塊
static{代碼體}
2、普通代碼與靜態代碼塊的使用區別
普通代碼塊:
1、是對物件進行初始化,每創建一次物件就執行一次
靜態代碼塊:
1、是對類進行初始化,隨著類被加載的時候就執行了,且只會執行一次(不管new幾個物件)
#代碼驗證:
//午餐類
class Lunch{
public static double price=20;
//普通代碼塊
{
System.out.println("普通代碼塊:先煮飯,洗菜,炒菜");
}
//靜態代碼塊
static {
System.out.println("靜態代碼塊:先煮飯,洗菜,炒菜");
}
//無參構造器
public Lunch() {
System.out.println("這是無參構造器");
}
//靜態方法
public static void makeLunch(){
System.out.println("這是一個靜態方法");
}
}
public class codeBlock {
public static void main(String[] args) {
Lunch lunch = new Lunch();
Lunch lunch1 = new Lunch();
Lunch lunch2 = new Lunch();
Lunch.makeLunch();
System.out.println(Lunch.price);
}
}
輸出:
靜態代碼塊:先煮飯,洗菜,炒菜
普通代碼塊:先煮飯,洗菜,炒菜
這是無參構造器
普通代碼塊:先煮飯,洗菜,炒菜
這是無參構造器
普通代碼塊:先煮飯,洗菜,炒菜
這是無參構造器
這是一個靜態方法
20.0
說明:
1、執行順序: 靜態代碼塊——》普通代碼塊——》構造器
2、執行次數:靜態代碼塊只執行一次,普通代碼塊執行次數跟物件創建次數一樣
3、靜態方法呼叫一次就執行一次
3、觸發類加載的幾種情況(靜態代碼塊)
1、創建物件實體時
2、當創建子類物件實體時,先加載父類,再加載子類
3、使用類的靜態成員(靜態屬性、靜態方法)
4、還有一種是反射( 我還沒學>_<...)
#代碼驗證:
public class codeBlock02 {
public static void main(String[] args) {
new CC();
//1、創建物件實體時會觸發類加載,執行靜態代碼塊,
//2、當創建子類物件實體時,當前類CC向上的所有父類AA、BB類都會觸發類加載,從而執行對應類的靜態代碼塊
//System.out.println(CC.str);
//1、當呼叫靜態成員(靜態方法和變數)時,會觸發類加載,執行靜態代碼塊內容
}
}
class AA{
//普通代碼塊
{
System.out.println("普通代碼塊AA");
}
//靜態代碼塊
static {
System.out.println("靜態代碼塊AA");
}
}
class BB extends AA{
//普通代碼塊
{
System.out.println("普通代碼塊BB");
}
//靜態代碼塊
static {
System.out.println("靜態代碼塊BB");
}
}
class CC extends BB{
public static String str="這是靜態變數";
//普通代碼塊
{
System.out.println("普通代碼塊CC");
}
//靜態代碼塊
static {
System.out.println("靜態代碼塊CC");
}
public CC() {
System.out.println("這是CC的無參構造器");
}
}
執行new CC()輸出:
靜態代碼塊AA
靜態代碼塊BB
靜態代碼塊CC
普通代碼塊AA
普通代碼塊BB
普通代碼塊CC
這是CC的無參構造器
執行System.out.println(CC.str)輸出:
靜態代碼塊AA
靜態代碼塊BB
靜態代碼塊CC
這是靜態變數
說明:上面結論是正確的!
4、創建一個類物件時,類中靜態代碼塊、靜態變數、普通代碼塊、普通變數、構造器的執行順序是什么?
上代碼測驗:
public class codeBlock02 {
public static void main(String[] args) {
AA aa = new AA();
}
}
class AA{
//普通變數
public int numb = getNumb();
//靜態類變數
public static String str=getStr();
//普通方法
public int getNumb(){
System.out.println("getNumb被執行了");
return 10;
}
//靜態方法
public static String getStr(){
System.out.println("getStr()方法被執行");
return "類變數";
}
//構造器
public AA(){
super();//當有父類時會執行,沒有不執行 <1>
<2>
System.out.println("這是AA的無參構造器"); <3>
}
// 在<2>這里我感覺隱藏了呼叫普通代碼塊的參考代碼,因為每new一個物件時,執行順序都是: <1> ---> <2> ---> <3>;
//普通代碼塊
{ System.out.println("普通代碼塊AA");}
//靜態代碼塊
static {System.out.println("靜態代碼塊AA");}
}
//輸出:
// getStr()方法被執行 ---》靜態類變數
// 靜態代碼塊AA ---》靜態代碼塊
// getNumb被執行了 ---》普通類變數
// 普通代碼塊AA ---》普通代碼塊
// 這是AA的無參構造器 ---》構造器
說明什么?
1、先執行靜態static的靜態代碼塊或靜態類變數,這兩優先級一樣,執行順序就看定義的位置誰先誰后
2、然后執行普通代碼塊或普通類變數,這兩優先級也一樣,執行順序就看定義的位置誰先誰后
3、最后執行構造器
4、在構造器中super()下面還隱藏了一段看不見的呼叫普通代碼塊的參考代碼
5、小練習(代碼塊)
#請問以下代碼輸出什么?
public class codeBlock01 {
public static void main(String[] args) {
new B();
}
}
class A{
//靜態屬性
public static int number_A = getA1();
//靜態代碼塊
static { System.out.println("A類靜態代碼塊");}
//普通代碼塊
{ System.out.println("A類普通代碼塊"); }
//普通屬性
public int age_A = getA2();
//父類構造方法
public A(){
System.out.println("列印A類構造器");
}
public static int getA1(){
System.out.println("呼叫了getA1方法");
return 20;
}
public int getA2(){
System.out.println("呼叫了getA2方法");
return 31;
}
}
class B extends A{
//靜態代碼塊
static { System.out.println("B類的靜態代碼塊"); }
//靜態屬性
public static String getStringB1 = getB1();
//普通代碼塊
{ System.out.println("B類的普通代碼塊"); }
//普通屬性
public String getStringB2 = getB2();
//子類構造方法
public B(){
super();
System.out.println("列印A類構造器");
}
public static String getB1(){
System.out.println("呼叫了getB1方法");
return "B1";
}
public String getB2(){
System.out.println("呼叫了getB2方法");
return "B2";
}
}
輸出:
呼叫了getA1方法
A類靜態代碼塊
B類的靜態代碼塊
呼叫了getB1方法
A類普通代碼塊
呼叫了getA2方法
列印A類構造器
B類的普通代碼塊
呼叫了getB2方法
列印A類構造器
6、小練習
以下程式會輸出?
public class Test {
static String s = "琳";
static {
String s = "麗";
System.out.println(s); // <1> 靜態代碼塊最先執行,輸出麗
}
public static void main(String[] args) {
new Son(s); //把"琳"傳進去
}
}
class Father{
static int m=100;
public Father(){ m=999;} //父類的m=999,不改變靜態變數m的值
static {
m=10000;
System.out.println("父類"+m);} // <2> 輸出:父類10000
}
class Son extends Father{
int m;
{ super.m=5;} //這里修改了父類屬性m=5
static {
System.out.println("static block"); //<3> 輸出:static block
}
public Son(String name){
System.out.println(name); //<4> 輸出: "琳"
System.out.println(m); <5> 輸出: 子類屬性沒有賦值,所以m=0;
System.out.println(super.m); <6>輸出: 5
}
}
最終輸出:麗,父類10000,static block,"琳",0,5
5、單例模式
# 使用靜態方法和靜態屬性實作單例模式
# 單例模式特點:一點創建了一個物件,之后就無法創建新物件,一個類只能有一個物件實體
#思路
1、限制創建物件
2、判斷是否已經存在一個物件實體
代碼實作:
public class SingletonMode {
public static void main(String[] args) {
//固定式
Singleton1 singleton1 = Singleton1.getSingleton();
Singleton1 singleton2 = Singleton1.getSingleton();
System.out.println(singleton1==singleton2);//回傳true,說明成功了
//靈活式
Singleton2 singleton3 = Singleton2.getSingleton("小花");
Singleton2 singleton4 = Singleton2.getSingleton("小梅");
System.out.println(singleton3==singleton4);//回傳true,說明成功了
/*
實作單例模式
思路:
1、限制創建新物件
2、判斷該類中是否已經存在一個物件實體
*/
}
}
//實作一:固定式
class Singleton1{ //缺點就是:這個name引數值在撰寫時必須寫死,那有沒有自己傳參值來new一個呢,看下一個
private String name;
private static Singleton1 singleton = new Singleton1("小琳");
//私有化構造器
private Singleton1(String name){
super();
this.name =name;
}
//提供一個static方法獲取物件
public static Singleton1 getSingleton(){
return singleton;
}
}
//實作二:靈活式 可以自定義單例物件的數值
class Singleton2{
private String name;
private static Singleton2 Singleton;
//私有化構造器
private Singleton2(String name){
super();
this.name = name;
}
//提供一個判斷物件是否存在且可以回傳物件的方法
public static Singleton2 getSingleton(String name){
//判斷當前是否存在物件
if (Singleton == null){
return Singleton = new Singleton2(name);
}
return Singleton;
}
}
小結(固定式和靈活式的區別):
物件創建時間上:
固定式是在類加載時就創建了該物件,而靈活式要呼叫才會創建物件
資源浪費上:
固定式有可能創建后就沒有用過所以存在浪費資源的可能,而靈活式就不會有這情況
靈活度上:
固定式是在撰寫代碼時物件就確定了,后續不能改變,而靈活式可以在第一次創建物件時選擇創建的物件值
執行緒安全上:
固定式是執行緒安全的,靈活式是執行緒不安全的!
6、抽象類
6.1、抽象類的介紹
1、抽象類定義
訪問修飾符 abstract 類名{ }//沒有類實作
2、抽象方法定義
訪問修飾符 abstract 方法名(引數串列)//沒有方法體
3、抽象類的價值更多的體現在設計層面,設計者設計好抽象類讓子類繼承并實作抽象類的功能,更多的是充當一個模板的作用,應用場景更多體現在設計模式和框架上!
6.2、抽象類的特點
1、抽象類不能被實體化創建
2、抽象類中,可以有抽象方法,也可以沒有抽象方法
3、一個方法一旦宣告了成抽象方法,該方法的類也必須宣告為抽象類!總結:抽象類不一定有抽象方法,但有抽象方法的類一定是抽象類
4、abstract只能修飾類和方法,不能修飾類屬性(只有抽象類和方法之說,沒有抽象屬性之說)
5、普通類里可以有的抽象類都可以有(如靜態方法、靜態屬性、靜態代碼塊、普通代碼塊),普通類里沒有的抽象方法抽象類有!
6、抽象方法沒有方法體
7、如果一個類繼承了抽象類,那就必須實作該抽象類的所有抽象方法,除非把自己也宣告為抽象類!
8、抽象方法不能使用private、final、static修飾,
#因為抽象方法定義后必須實作,而重寫也是一種實作的方式,private私有化不能被重寫,final不能被繼承也就無法重寫,static靜態化方法只跟類相關,沒有物件去實作這個抽象方法,重寫是跟物件相關的!
6.3、多型在抽象類的體現
1、多型引數:
[抽象父類的參考也可以指向繼承它的所有子類物件,所以可以使用抽象父類的參考作為形參去接收傳遞過來的所有子類物件引數] 什么意思?看下面代碼
代碼:
----------------------------------------------------------------------------------------
/* 驗證:
多型引數:接收子類物件
多型陣列:存盤各個子類物件
*/
//抽象父類-寵物類
abstract class Pets{
private String name;
public Pets(String name) {
super();
this.name = name;
System.out.println(name);
}
}
//寵物狗類
class petsDog extends Pets{
public petsDog(String name){
super(name);
}
}
//寵物貓類
class petsCat extends Pets{
public petsCat(String name){
super(name);
}
}
//測驗類
public class Abstract01 {
public static void main(String[] args) {
Pets cat = new petsCat("加菲貓");
Pets dog = new petsDog("旺財");
//抽象父類參考Pets可以作為形參接收petsCat("加菲貓")和petsDog("旺財")
// 這兩個子類物件實體,就證明了抽象類也具有多型引數的特性
}
}
輸出:
加菲貓
旺財
2、多型陣列:
[抽象父類也可以存放所有繼承的子類物件]什么意思?看下面代碼
代碼:
/* 驗證:
多型引數:接收子類物件
多型陣列:存盤各個子類物件
*/
//抽象父類-寵物類
abstract class Pets{
private String name;
public Pets(String name) {
super();
this.name = name;
System.out.println(name);
}
public String getName(){
return name;
}
//定義一個公有的抽象方法play(),玩
public abstract void play();
}
//寵物狗類
class petsDog extends Pets{
public petsDog(String name){
super(name);
}
//重寫實作play()方法
@Override
public void play() {
System.out.println(getName()+"可以拿來擼");
}
//狗特有行為:看大門
public void guardHome(){
System.out.println(getName()+"會幫主人看家");
}
}
//寵物貓類
class petsCat extends Pets{
public petsCat(String name){
super(name);
}
@Override
public void play(){
System.out.println(getName()+"可以拿來擼");
}
//貓特有行為:抓老鼠
public void captureMouse(){
System.out.println(getName()+"會抓老鼠");
}
}
//測驗類
public class Abstract01 {
public static void main(String[] args) {
//在這里定義一個抽象父類Pets陣列,去接收不同的子類物件
Pets[] pets = new Pets[]{new petsCat("加菲貓"),new petsDog("旺財"),};
//抽象父類參考pets去接收并存盤 new petsCat("加菲貓") 和 new petsDog("旺財")子類物件,運行時會進行動態系結!
new Abstract01().getObject(pets);
}
//定義一個判斷子類物件型別的方法,并列印出物件對應的特有方法
public void getObject(Pets[] abs){
//使用instanceof進行子類物件型別判斷
for (Pets p:abs){
if (p instanceof petsCat){
((petsCat)p).captureMouse();
}else { //因為抽象父類無法被實體化,所以不可能會有父類的物件出現,物件只可能兩種型別
((petsDog) p).guardHome();
}
}
}
}
6.4、抽象類體現了模板設計模式
1、抽象充當了多個子類的通用模板,子類可以在繼承抽象類的基礎上進行擴展,但子類中還是保留一定的抽象父類的行為方法
2、什么是模板設計模式?
#當有一部分功能是確定的,一部分功能是不確定的,可以用模板設計模式設計一個抽象父類,里面定義一個抽象方法,這個抽象方法做不確定功能,確定的功能在抽象父類寫一個方法實作好;然后讓子類繼承并實作這個功能不確定的抽象方法
上代碼演示:
3、需求:我想要一個抽象類,用于計算一個方法執行的時間(什么方法不確定,但是用于計算時間的方法確定)
//抽象計算類
//抽象計算類
abstract class Calculation{
//抽象方法
abstract public void Method();
//計算耗時的方法
public void consumeTime(){
long start_time = System.currentTimeMillis();
Method();
long end_time = System.currentTimeMillis();
System.out.println("該方法耗時為:"+(end_time-start_time));
}
}
class Son1 extends Calculation{
@Override
public void Method(){
String str = "";
for (int i = 0; i < 100000; i++) {
str += "SB";
}
}
}
class Son2 extends Calculation{
@Override
public void Method(){
StringBuffer str = new StringBuffer("");
for (int i = 0; i < 100000; i++) {
str.append("SB");
}
}
}
class Son3 extends Calculation{
@Override
public void Method(){
StringBuilder str = new StringBuilder("");
for (int i = 0; i < 100000; i++) {
str.append("SB");
}
}
}
//測驗類
public class Template {
public static void main(String[] args) {
//實作一:
//new Son1().consumeTime();
//son1先找自己consumeTime方法,沒有就找父類consumeTime方法,
// 父類consumeTime根據動態系結呼叫自己的Method方法
//new Son2().consumeTime();
//new Son3().consumeTime();
//實作二:
Template.test(new Son1()); //呼叫靜態方法:類名.方法名
new Template().test(new Son2());//呼叫靜態方法:物件.方法名
test(new Son3()); //因為該方法在同類中,直接方法名
}
public static void test(Calculation c){
c.consumeTime();
}
}
輸出:
該方法耗時為:12139
該方法耗時為:3
該方法耗時為:2
//說明字串拼接操作效率:StringBuilder > StringBuffer > String;
7、介面
1、介面介紹:
就是把一些沒有實作的方法通過介面封裝到一起,當哪個類需要使用到介面的方法的時候,該類根據需求再具體去實作介面里的方法
介面定義格式:
interface 介面名{
屬性
抽象方法(介面里的方法默認是abstract抽象的)
在jdk1.8后,介面里可以有 靜態方法 和 默認方法的實作
}
實作介面格式:
class 類名 implements 介面名{
//可以有自己的屬性和方法,但是必須實作(重寫)介面的所有方法,可以跟介面隔離原則去優化
自己屬性
自己方法
必須實作介面的抽象方法
}
#栗子:
//介面
interface interfaceDemo{
//介面里的屬性必須定義時給定初始值
public String str="111";
//等同public static final String str = "111"
//靜態方法
public static void print01(){
System.out.println("靜態方法");
}
//默認方法
public default void pint02(){
System.out.println("默認方法");
}
//抽象方法
void absMethod1();
void absMethod2();
}
//測驗類
public class Interface02 {
public static void main(String[] args) {
//介面里的靜態方法直接用介面名呼叫,跟類的靜態方法呼叫一樣
interfaceDemo.print01();
//默認方法介面自己無法使用介面名呼叫
//interfaceDemo.print02();
}
}
//普通類
class Ordinary implements interfaceDemo{
//實作一個抽象方法1還是報錯,接著實作方法2報錯,實作默認方法后不報錯,說明要全部實作(包含默認方法)
@Override
void pint02() { //一旦我把print2方法的改為默認,就報錯,而根據實作介面的子類方法訪問權限必須 >= 介面抽象方法的訪問權限,
//那就證明介面的抽象方法的訪問權限默認是public
}
@Override
public void absMethod1() {
}
@Override
public void absMethod2() {
}
}
//抽象類
abstract class absClass implements interfaceDemo{
//不報錯,說明抽象類可以不用實作介面的抽象方法
}
//測驗類
public class Interface02 {
public static void main(String[] args) {
//介面里的靜態方法直接用介面名呼叫,跟類的靜態方法呼叫一樣
interfaceDemo.print01();
//默認方法介面自己無法使用介面名呼叫,只能給實作了介面的子類呼叫
//interfaceDemo.print02();
}
}
小細節:
1、在jdk1.8后,介面里可以有 靜態方法 和 默認方法的實作 及定義介面自己屬性
2、介面里的屬性必須定義時給定初始值,等同于常量屬性
3、介面里的靜態方法直接用介面名呼叫,跟類的靜態方法呼叫一樣
4、默認方法介面自己無法使用介面名呼叫,只能給實作了介面的子類呼叫
5、實作介面的子類方法權限 >= 介面的抽象方法訪問權限
6、抽象類可以實作介面,但可以實作介面的抽象方法
7、介面可以繼承別的介面,但是不能繼承普通類,當然也包含抽象類
//注意:
1、當多個介面里如果有重名的抽象方法,不會影響使用,如果是重名的常量屬性時,呼叫時使用介面名.屬性名區分
7.2、繼承與介面在應用上有什么區別?
1、介面其實是對java單繼承缺點的一種彌補和擴展,讓子類有更豐富的方法和屬性,而這些屬性來源于子類實作介面里的抽象方法!
#栗子
package P3;
//猴子父類
class Monkey{
private String name;
public Monkey(String name){
this.name = name;
}
public String getName(){
return name;
}
//爬樹
public void climbTree(){
System.out.println(getName()+"會爬樹");
}
}
//想要給悟空加技能,會上天飛,會下海潛,使用實作介面的抽象方法
interface FlightClass{
public void flight();//會飛行
}
interface DivingClass{
public void diving();//會潛水
}
//孫悟空類
class sunMonkey extends Monkey implements DivingClass,FlightClass{
public sunMonkey(String name) {
super(name);
}
@Override
public void climbTree() {
super.climbTree();
}
//實作飛行和潛水方法
@Override
public void diving(){
System.out.println(getName()+"會潛水");
}
@Override
public void flight(){
System.out.println(getName()+"會飛行");
}
}
//測驗類
public class Relation {
public static void main(String[] args) {
new sunMonkey("孫悟空").diving();
new sunMonkey("孫悟空").flight();
new sunMonkey("孫悟空").climbTree();
//介面多型,跟普通類多型一樣具有多型引數和多型陣列特性
test1(new sunMonkey("孫大圣"));
test2(new sunMonkey("孫大圣"));
test3(new sunMonkey("孫大圣"));
}
//在別的類的方法中去接收父類物件和實作介面方法的物件,這就是多型
public static void test1(Monkey monkey){
monkey.climbTree();
}
public static void test2(DivingClass divingClass){
divingClass.diving();
}
public static void test3(FlightClass flightClass){
flightClass.flight();
}
}
輸出:
孫悟慷訓潛水
孫悟慷訓飛行
孫悟慷訓爬樹
孫大圣會爬樹
孫大圣會潛水
孫大圣會飛行
7.3、介面的多型性
# 介面的多型跟類的多型差不多,也有多型引數和多型陣列的特性
1、多型引數 與 多型陣列
代碼舉栗:
package P3;
import org.omg.CORBA.PUBLIC_MEMBER;
//游戲介面
interface IGame{
public void loading();
public void start();
public void end();
}
//游戲類
class Game{
private String gameName;
public Game(){};
public Game(String gameName){ this.gameName = gameName;}
public String getGameName(){ return gameName;}
//游戲運行
public void runGame(IGame iGame){
//這里使用介面參考去接收實作該介面的類的物件,體現了介面的多型引數
iGame.loading();
iGame.start();
iGame.end();
}
}
//LOL
class LOL extends Game implements IGame {
public LOL(){}
public LOL(String gameName){ super(gameName);}
@Override
public void loading() {
System.out.println(getGameName()+"正在加載....");
}
@Override
public void start() {
System.out.println(getGameName()+"加載完成,可以開始游戲啦....");
}
@Override
public void end() {
System.out.println(getGameName()+"正在關閉游戲....");
}
//獨特方法
public void lolObject(){
System.out.println(getGameName()+"是5v5推塔游戲");
}
}
//CF
class CF extends Game implements IGame{
public CF(){}
public CF(String gameName) {
super(gameName);
}
@Override
public void loading() {
System.out.println(getGameName()+"正在加載....");
}
@Override
public void start() {
System.out.println(getGameName()+"加載完成,可以開始游戲啦....");
}
@Override
public void end() {
System.out.println(getGameName()+"正在關閉游戲....");
}
//獨特方法
public void cfObject(){
System.out.println(getGameName()+"是多人槍戰游戲");
}
}
//測驗類
public class Relation {
public static void main(String[] args) {
//介面多型引數的體現
Game game = new Game("LOL");
game.runGame(new LOL("LOL"));
game.runGame(new CF("CF"));
System.out.println("----------------------");
//介面多型陣列的體現
IGame[] iGames = new IGame[]{new LOL("英雄聯盟"),new CF("穿越火線")};
for (IGame i: iGames) {
if (i instanceof LOL){
//如果想呼叫子類物件的獨特方法,可以使用instanceof進行物件型別判斷,
((LOL) i).lolObject();
}else { ((CF)i).cfObject();}
}
}
}
輸出:
LOL正在加載....
LOL加載完成,可以開始游戲啦....
LOL正在關閉游戲....
CF正在加載....
CF加載完成,可以開始游戲啦....
CF正在關閉游戲....
----------------------
英雄聯盟是5v5推塔游戲
穿越火線是多人槍戰游戲
2、介面存在多型傳遞特性
代碼舉栗:
package p4;
import com.sun.xml.internal.ws.api.model.wsdl.WSDLOutput;
//測驗類
public class Interface{
public static void main(String[] args) {
Son son = new Son();
//這是實作了子介面Ison的子類Son物件
ISon iSon = son;
//這里可以把son物件賦值給子介面ISon的參考,說明子介面參考是可以指向并接收Son類物件的
IFather iFather = son;
//這里可以把son物件賦值給父介面IFather的參考,說明父介面參考是可以指向并接收Son類物件的
Father father = son;
//這里可以把son物件賦值給父類Father的參考,說明父類參考是可以指向并接收Son類物件的
}
}
//父介面
interface IFather{
public void father();
}
//子介面
interface ISon extends IFather{
public void son();
}
//父類
class Father {
public void print(){
System.out.println("這是父類的print()方法");
}
}
//子類
class Son extends Father implements ISon{
@Override
public void son(){
System.out.println("這是子介面的son()方法");
}
@Override
public void father(){
System.out.println("這是父介面的father()方法");
}
}
小結:
1、當一個介面B繼承介面A,而子類b繼承父類a,而b實作介面B方法時,b類物件可以傳遞給介面B的所有父介面的介面參考,這就是介面的多層傳遞
3、多型引數與多型陣列在使用上的區別?
1、多型陣列在程式運行時,所有繼承的子類物件或實作介面抽象方法的類物件,可以以陣列的方式同時傳遞多個物件給父類參考
2、而引數多型只能同時傳遞一種類物件
3、所以當需要使用多個不同類物件時,使用多型陣列;反之,使用多型引數就可以了
4、小練習
//課后小練習:以下程式輸出什么?如有錯誤,請修改
interface A{ int a = 0;}
class B{ int a = 1;}
class C extends B implements A{
public void method1(){
System.out.println(a);
}
public static void main(String[] args) {
new C().method1();
}
}
解:
錯誤:呼叫a會有歧義,如果想呼叫介面的a,使用介面名A.a ; 如果想用B類的a,使用super.a
8、內部類
8.1 、內部類介紹
1、內部類介紹:
#一個類中有嵌入一個完整的類結構,這個嵌套的類稱為內部類
內部類特點:
1、內部類可以直接訪問外層類的私有屬性
2、內部類的作用域在它的外部類中
3、本質上其實也是外部類的一個成員,其他都一樣
內部類定義格式:
class Outer{ //外部類
class Inner{ //內部類
public void Method1(){
class Inner01{} //方法里的內部類
}
}
}
呼叫內部類格式:
new 外部類().new 內部類().內部類方法名或屬性
代碼舉栗:
//外部類
class A{
private int a = 1;
private void A(){
System.out.println("這是外層類的A方法");
}
//內部類
class Inner{
public void print(){
System.out.println(a);
}
}
public static void main(String[] args) {
new A().new Inner().print();
}
}
輸出:
1
2、內部類的分類:
*在外部類的方法中
1、區域內部類(有類名)
2、匿名內部類(沒有類名,這是重點,常用)
*類似外部類的成員類
1、成員內部類(沒用static修飾)
2、靜態內部類(用static修飾)
8.2、區域內部類
1、區域內部類的介紹:
訪問權限: 可以訪問外部類所有成員和自己的成員
位置: 定義在外部類的方法或代碼塊中
修飾符: 只能使用final常量修飾符,因為它的作用域僅限于區域方法中,區域變數不能用修飾符,但final可以
作用域: 僅在外部類的方法或代碼塊中
#使用區域內部類舉栗:
//外部類
class A{
private int a = 1;
public void f1(){
//外部類訪問區域內部類
};
//外部類
class C1{ public void m1(){}};
private void print(){
class LocalInnerClass{
public void LocalInnerMethod(){
System.out.println("這是區域內部類的方法");
//區域內部類-->訪問-->外部類成員-->直接訪問
f1();
System.out.println(a);
}
}
//如果想在外部類呼叫區域內部類里的方法,這里得先這樣
new LocalInnerClass().LocalInnerMethod();
}
public static void main(String[] args) {
new A().print();
}
}
8.3、匿名內部類
1、匿名內部類的介紹
方式一(使用介面名new)、
//介面
interface Interface1{
public void print();
}
class Anonymous {
public void Method() {
/*這是匿名內部類方式一、
* 解讀:
*1、編譯型別是介面Interface1,
*2、運行型別是:
* new Interface1() {
@Override
public void print() {
System.out.println("這是匿名內部類");
}
};
* 3、匿名內部類是一個物件,也相當于實作介面Interface1的無名子類
* */
Interface1 i = new Interface1() {
@Override
public void print() {
System.out.println("這是匿名內部類");
}
};
i.print();
}
//測驗
public static void main(String[] args) {
new Anonymous().Method();
}
}
方式二(使用類名new)、
package p4;
//介面
interface Interface1{
public void print();
}
class FatherClass{
public void test(){}
}
class Anonymous {
public void Method() {
/*這是匿名內部類方式一、
* 解讀:
*1、可以理解為:相當于介面Interface1的子類的實體物件
*2、編譯型別是介面Interface1,
*3、運行型別是: class p4.Anonymous$1 表示Anonymous編號為1的內部類
Interface1() {
@Override
public void print() {
System.out.println("這是匿名內部類");
}
};
* 3、匿名內部類是一個物件,也相當于實作介面Interface1的無名子類
* */
Interface1 i = new Interface1() {
@Override
public void print() {
System.out.println("這是匿名內部類方式一");
}
}; //4、匿名內部類尾部記得加;
i.print();
System.out.println(i.getClass());
}
public void Method1(){
/*匿名內部類方式二
* 1、可以理解為:相當于類FatherClass的子類的實體物件
* 2、編譯型別是FatherClass類
* 3、運行型別是: class p4.Anonymous$2 表示Anonymous編號為2的內部類
FatherClass() {
@Override
public void test() {
System.out.println("這是匿名內部類方式二");
}
};
* */
FatherClass father = new FatherClass() {
@Override
public void test() {
System.out.println("這是匿名內部類方式二");
}
};father.test();
System.out.println(father.getClass());
}
//測驗
public static void main(String[] args) {
new Anonymous().Method();//匿名內部類方式一
new Anonymous().Method1();//匿名內部類方式二
}
}
輸出:
這是匿名內部類方式一
class p4.Anonymous$1
這是匿名內部類方式二
class p4.Anonymous$2
2、匿名內部類的呼叫特點:
1、通過物件本身呼叫內部類的方法
new FatherClass() {
@Override
public void test() {
System.out.println("這是匿名內部類方式二");
}
}.test();
2、通過物件參考呼叫本身內部的方法
FatherClass father = new FatherClass() {
@Override
public void test() {
System.out.println("這是匿名內部類方式二");
}
};father.test();
3、匿名內部類的應用場景
1、當作實參直接傳遞
代碼舉栗:
public class Anonymous01 {
public static void main(String[] args) {
Kitchen kitchen = new Kitchen();
/**
* 1、匿名內部類只執行一次,適合作引數進行傳遞
* */
kitchen.rinseFood(new Soup(){
public void cooking(){
System.out.println("午餐吃陽澄湖大閘蟹");
}
public void stewSoup(){
System.out.println("加三鮮湯");
}
});
kitchen.rinseFood(new Soup() {
@Override
public void cooking() {
System.out.println("午餐吃糖醋排骨");
}
@Override
public void stewSoup() {
System.out.println("加排骨蘿卜湯");
}
});
}
}
//烹飪介面
interface ICook{ void cooking();}
//煮湯介面
interface Soup extends ICook{ void stewSoup();}
//廚房類
class Kitchen {
//洗菜方法
public void rinseFood(Soup soup){
soup.cooking();
soup.stewSoup();
}
}
輸出:
午餐吃陽澄湖大閘蟹
加三鮮湯
午餐吃糖醋排骨
加排骨蘿卜湯
4、如何使用多型機制把區域內部類接到外部類使用
##### 1、使用介面接收
?```
package p5;
public class Test {
public static void main(String[] args) {
/**
* 1、frock型別class p5.Frock; inner型別是class p5.Frock$1InnerH3
* 2、frock.h2()回傳物件型別是其實是實作介面InnerH3類的物件型別,
* 屬于外部類Frock的成員,所以不需要向下轉型
* */
Frock frock = new Frock();
Inner inner = frock.h2();
inner.show();
System.out.println(frock.getClass());
System.out.println(inner.getClass());
}
}
//1、使用介面把內部類InnerH3接出來
interface Inner{ public void show();}
//使用子類把InnerH3接出來
//外部類Frock
class Frock {
int a1 = 20;
//區域內部類
public Inner h2(){
class InnerH3 implements Inner{
int a1 = 10;
public void show(){
System.out.println("------------------");
//內部類成員與外部類成員重名時,在內部類呼叫屬性遵循就近原則
System.out.println("區域內部類-a1:"+a1);
//呼叫外部類成員名使用必須 外部類名.this.重名屬性名,this.重名屬性名都不行
System.out.println("外部類屬性-a1:"+Frock.this.a1);
}
}
InnerH3 innerH3 = new InnerH3();
innerH3.show();
return innerH3;
}
}
輸出:
------------------
區域內部類-a1:10
外部類屬性-a1:20
------------------
區域內部類-a1:10
外部類屬性-a1:20
class p5.Frock
class p5.Frock$1InnerH3
?```
##### 2、使用抽象類接收
package p5;
public class Test1 {
public static void main(String[] args) {
/**分析:
* 1、frock1物件型別是class p5.Frock1,
* 而inner1型別是 class p5.Frock1$1InnerH3,所以需要向下轉型
* 2、 frock1.h2()回傳物件是抽象類的子類,轉成抽象型別別Inner1時,所以需要向下轉型
* */
Frock1 frock1 = new Frock1();
Inner1 inner1= (Inner1) frock1.h2();
inner1.show();
System.out.println(frock1.getClass());
System.out.println(inner1.getClass());
}
}
//1、使用介面把內部類InnerH3接出來
//interface Inner{ public void show();}
//2、使用抽象類把InnerH3接出來
abstract class Inner1{ //
/**第一步,建一個抽象類和抽象方法,
* 給h2()方法提供一個接受內部類物件
* */
public void show(){}
}
//外部類Frock
class Frock1 {
int a1 = 20;
//區域內部類
public Inner1 h2(){
/**
* 第二步,讓內部類繼承抽象類,
* 然后回傳內部類物件,讓抽象類接收
* */
class InnerH3 extends Inner1{
int a1 = 10;
public void show(){
System.out.println("------------------");
//內部類成員與外部類成員重名時,在內部類呼叫屬性遵循就近原則
System.out.println("區域內部類-a1:"+a1);
//呼叫外部類成員名使用必須 外部類名.this.重名屬性名,this.重名屬性名都不行
System.out.println("外部類屬性-a1:"+Frock1.this.a1);
}
}
InnerH3 innerH3 = new InnerH3();
innerH3.show();
return innerH3;
}
}
8.4、 成員內部類
1、成員內部類介紹:
1、顧名思義,就是定義在外部類的成員位置(除方法中外),沒有static修飾,可以直接訪問外部類的所有成員,包括私有屬性!
2、它可以使用任何訪問修飾符,因為它的地位屬于成員而不是區域
3、如果成員內部類的成員與外部類的成員有重名的,那么在內部類呼叫內部類成員,直接呼叫;在內部類呼叫外部類重名成員時,使用 外部類名.this.重名成員名
格式長這樣:
class OtherClass{
private String str="老黃牛"
class InnerClass{
public void show(){
System.out.println("向"+str+"學習")
}
}
}
代碼舉栗(成員內部類):
package p5;
public class Test01 {
public static void main(String[] args) {
//呼叫成員內部類方式一、
OtherClass otherClass = new OtherClass();
otherClass.new InnerClass().show();
//方式二、
otherClass.Method().show();
}
}
class OtherClass{
private String str="老黃牛";
private void PMethod(){
System.out.println("我是私有方法");
}
//成員內部類
class InnerClass{
public InnerClass show(){
//1、成員內部類訪問外部類,直接訪問
System.out.println("向"+str+"學習");
PMethod();
return null;
}
}
public InnerClass Method(){
//2、外部類訪問成員內部類的成員,先創建內部類物件.成員
return new InnerClass();
}
}
2、如果內部成員類被私有化了,外部其他類還是想訪問該私有成員類中的方法呢,怎么辦呢?還是用介面接出來
代碼舉栗:
方式一、介面
package p5;
import javax.jws.Oneway;
public class Test01 {
public static void main(String[] args) {
//方式一、介面
OtherClass otherClass = new OtherClass();
I i = otherClass.PMethod();
i.show();
}
}
//方式一、介面
interface I{ public void show();}
//方式二、抽象類
//abstract class ABS{ public void show(){}}
class OtherClass{
private String str="老黃牛";
public InnerClass PMethod(){
return new InnerClass();
}
//成員內部類
private class InnerClass implements I{
public void show(){
//1、成員內部類訪問外部類,直接訪問
System.out.println("向"+str+"學習");
PMethod();
}
}
}
輸出:
向老黃牛學習
-------------------------------------------------------------------------------
方式二、抽象類
package p5;
public class Test02 {
public static void main(String[] args) {
//方式二、抽象類
OtherClass01 otherClass01 = new OtherClass01();
ABS abs = (ABS)otherClass01.PMethod();
abs.show();
}
}
//方式二、抽象類
abstract class ABS{ public void show(){}}
//外部類
class OtherClass01{
private String str="老黃牛";
public InnerClass PMethod(){
return new InnerClass();
}
//成員內部類
private class InnerClass extends ABS{
public void show(){
//1、成員內部類訪問外部類,直接訪問
System.out.println("向"+str+"學習");
PMethod();
}
}
}
輸出:
向老黃牛學習
3、如果成員內部類的成員與外部類的成員有重名的,那么呼叫在內部類呼叫外部類成員,直接呼叫; 在外部類呼叫內部類成員時,使用 外部類名.this.內部類成員名
8.5、 靜態內部類
1、靜態內部類的介紹
1、靜態內部類 = 使用static修飾內部類
2、跟成員內部類其他的相同,唯一區別就是:靜態內部類只能訪問外部類的靜態成員
2、靜態內部類的使用方式:
package p5;
public class Test03 {
public static void main(String[] args) {
//方式二、
new OtherClass02().Method01();
//方式一
OtherClass02.Method01();
}
}
//外部類
class OtherClass02{
private static int a1 = 1;
private int a2 = 2;
static class InnerClass02{
public void Method(){
System.out.println("外部類屬性a1:"+a1);
}
}
//在外部其他類,使用靜態內部類的Method()方法
//方式一、使用靜態方法直接呼叫靜態方法
public static void Method01(){
new InnerClass02().Method();
}
//方式二、使用該方法回傳一個靜態內部類物件
public InnerClass02 Method02(){
return new InnerClass02();
}
}
3、當靜態內部類成員與外部類成員出現重名時,在內部類呼叫內部類重名的成員時,直接呼叫; 在靜態內部呼叫外部類靜態重名成員,使用外部類名.重名成員名
#這里就不寫代碼舉栗了
9、列舉
10、注解
1、注解的介紹:
1、注解也稱為元資料,用于修飾解釋包、類、方法、屬性、構造器、區域變數等資訊
2、和注釋一相似之處在于它不影響代碼邏輯,不同之處注解可以被編譯歡訓運行,相當于嵌入代碼中的補充資訊
3、使用注解主要是為了標記過時功能、忽略警告資訊等
10.1、Override
1、用于限定某個方法,常用于重寫父類方法,該注解只能用于方法中
10.2、Deprecated
1、用于表示某個程式中的類、方法已過時
10.3、supresswarning
1、用于屏蔽編譯器的警告資訊
11、包裝類
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/261956.html
標籤:Java
上一篇:原始碼讀起來,Go原始碼共讀計劃
