1、類變量(靜態(tài)類變量)
1、為什么要有靜態(tài)類變量?
引出:
過(guò)年,公司發(fā)過(guò)年禮品,假設(shè)我們有一個(gè)禮品類Gift,有一個(gè)發(fā)放禮品的類方法receive(),我想知道當(dāng)前有多少個(gè)員工領(lǐng)取禮品,怎么辦?
解決:
1、在類中定義一個(gè)屬性count,然后在receive()里每調(diào)用該方法進(jìn)行conut++?
2、在main方法定義一個(gè)count局部變量,每調(diào)用一次receive()進(jìn)行count++?
3、在類中定義一個(gè)靜態(tài)的類變量,在方法receive()中進(jìn)行count++?
答案:選擇3!第一種不管領(lǐng)取了多少禮品,每個(gè)對(duì)象中的count屬性值都是1;第二種可以是可以,但不符合oop的思想,如果需要在別的地方用到該count值,沒(méi)辦法調(diào)用??;第三種,靜態(tài)類變量多個(gè)對(duì)象共享該變量,不管是用那個(gè)對(duì)象獲取的count值,都是最新的且一致的!
#測(cè)試代碼:
public class StaticDemo {
public static void main(String[] args) {
Gift gift = new Gift("張三");
Gift gift1 = new Gift("李四");
gift.receive();
System.out.println("當(dāng)前有"+Gift.count+"個(gè)員工領(lǐng)取了禮品"+" 名字分別為:"+ Gift.str);
gift1.receive();
System.out.println("當(dāng)前有"+gift1.count+"個(gè)員工領(lǐng)取了禮品"+" 名字分別為:"+ 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;
}
//發(fā)放禮品
public void receive(){
giftEliminate();
count++;
str = str.concat(getName());
}
//當(dāng)領(lǐng)取人數(shù)大于5個(gè)我就調(diào)用該方法將count清零;
public void giftEliminate(){
if (count>5){
Gift.count = 0;
}
}
}
輸出:
當(dāng)前有1個(gè)員工領(lǐng)取了禮品 名字分別為:張三
當(dāng)前有2個(gè)員工領(lǐng)取了禮品 名字分別為:張三李四
小結(jié)(什么時(shí)候使用類變量?):
1、靜態(tài)類變量的作用就是存儲(chǔ)一些獨(dú)立于對(duì)象的數(shù)據(jù),但各對(duì)象又能共享該變量值
2、如何定義和使用靜態(tài)類變量?
定義:
1、static public int count
2、public static int count (推薦使用)
#從上面代碼可以看出,在普通屬性上聲明一個(gè)static關(guān)鍵字,靜態(tài)類方法也是如此
使用:
1、使用類名.變量名(推薦使用) Gift.count
2、對(duì)象名.變量名 gift1.count
3、類變量于實(shí)例變量(普通類屬性)的區(qū)別?
1、類變量屬于類,但被所有對(duì)象共享
2、實(shí)例變量只屬于這一個(gè)對(duì)象,獨(dú)立不被共享
4 小結(jié):
類變量:
1、使用關(guān)鍵字static聲明定義,推薦定義格式: 修飾符 static 數(shù)據(jù)類型 變量名
2、使用類名.變量名調(diào)用該類變量
3、被所有對(duì)象共享變量值
實(shí)例變量:
1、定義:修飾符 數(shù)據(jù)類型 變量名
2、使用對(duì)象名.變量名調(diào)用該變量
3、只屬于該對(duì)象本身,不被共享
2、靜態(tài)方法
1、為什么要有靜態(tài)方法?
應(yīng)用:
當(dāng)我們要寫(xiě)一個(gè)方法操作一些跟對(duì)象無(wú)關(guān)的數(shù)據(jù)時(shí),可以使用靜態(tài)方法。
2、靜態(tài)方法的定義和使用
定義:
1、public static void print(){}
2、static void print(){}
修飾符 static 返回?cái)?shù)據(jù)類型 方法名(){}
#注意:因?yàn)樾揎椃椒ǖ男揎椃挥衟ublic 或 默認(rèn)(不寫(xiě))
使用:
1、類名.方法名() (推薦使用)
2、對(duì)象名.方法名()
代碼:
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("當(dāng)前學(xué)費(fèi)共交了"+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;
}
//計(jì)算學(xué)費(fèi)
public static void count_study_money(double study_money){
//Student.study_money = Student.study_money + study_money;
Student.study_money += study_money;
}
//返回學(xué)費(fèi)
public static double get_study_money(){
return Student.study_money;
}
public void print(){
System.out.println(get_study_money());//非靜態(tài)方法中調(diào)用靜態(tài)方法
System.out.println(name);//非靜態(tài)方法中調(diào)用非靜態(tài)變量
System.out.println(str);//非靜態(tài)方法中調(diào)用靜態(tài)變量
}
}
輸出:
當(dāng)前學(xué)費(fèi)共交了8000.0元
8000.0
張三
3、使用類方法時(shí),有哪些要注意的細(xì)節(jié)?
1、類方法中不能使用this和super與對(duì)象相關(guān)的關(guān)鍵字。 因?yàn)閠his代表調(diào)用者當(dāng)前對(duì)象本身,而靜態(tài)方法執(zhí)行是無(wú)需實(shí)例化對(duì)象,那這個(gè)this就是空,所以靜態(tài)方法中不能用this
2、類方法和類變量調(diào)用跟對(duì)象是否創(chuàng)建無(wú)關(guān),調(diào)用時(shí)都是通過(guò)類名.類變量/類方法()調(diào)用
3、而對(duì)象變量和對(duì)象方法與實(shí)例對(duì)象有關(guān),調(diào)用對(duì)象方法和對(duì)象變量時(shí)只能用對(duì)象名.變量 或 對(duì)象名.方法名(),不能使用類名.方法和變量!
4、靜態(tài)(類)方法中只能調(diào)用靜態(tài)(類)變量和靜態(tài)方法,無(wú)法使用非靜態(tài)變量和非靜態(tài)方法
5、非靜態(tài)方法可以調(diào)用非靜態(tài)變量、非靜態(tài)方法、靜態(tài)方法、靜態(tài)變量
i
3、Main()方法
public static void main(String[] args){}
1、為什么main()方法要這樣寫(xiě)?
# 因?yàn)橛辛藀ublic權(quán)限,所以jvm才能調(diào)用main;
因?yàn)橛辛藄tatic,所以調(diào)用mian()方法時(shí)不需要有對(duì)象創(chuàng)建;
因?yàn)橛辛薙tring[] args字符串?dāng)?shù)組,所以運(yùn)行main里面的程序時(shí),可以用字符串?dāng)?shù)組去存儲(chǔ)中轉(zhuǎn)所需的參數(shù)!
4、代碼塊
1、代碼塊的定義
1、普通代碼塊
格式:{代碼體}
2、靜態(tài)代碼塊
static{代碼體}
2、普通代碼與靜態(tài)代碼塊的使用區(qū)別
普通代碼塊:
1、是對(duì)對(duì)象進(jìn)行初始化。每創(chuàng)建一次對(duì)象就執(zhí)行一次
靜態(tài)代碼塊:
1、是對(duì)類進(jìn)行初始化。隨著類被加載的時(shí)候就執(zhí)行了,且只會(huì)執(zhí)行一次(不管new幾個(gè)對(duì)象)
#代碼驗(yàn)證:
//午餐類
class Lunch{
public static double price=20;
//普通代碼塊
{
System.out.println("普通代碼塊:先煮飯,洗菜,炒菜");
}
//靜態(tài)代碼塊
static {
System.out.println("靜態(tài)代碼塊:先煮飯,洗菜,炒菜");
}
//無(wú)參構(gòu)造器
public Lunch() {
System.out.println("這是無(wú)參構(gòu)造器");
}
//靜態(tài)方法
public static void makeLunch(){
System.out.println("這是一個(gè)靜態(tài)方法");
}
}
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);
}
}
輸出:
靜態(tài)代碼塊:先煮飯,洗菜,炒菜
普通代碼塊:先煮飯,洗菜,炒菜
這是無(wú)參構(gòu)造器
普通代碼塊:先煮飯,洗菜,炒菜
這是無(wú)參構(gòu)造器
普通代碼塊:先煮飯,洗菜,炒菜
這是無(wú)參構(gòu)造器
這是一個(gè)靜態(tài)方法
20.0
說(shuō)明:
1、執(zhí)行順序: 靜態(tài)代碼塊——》普通代碼塊——》構(gòu)造器
2、執(zhí)行次數(shù):靜態(tài)代碼塊只執(zhí)行一次,普通代碼塊執(zhí)行次數(shù)跟對(duì)象創(chuàng)建次數(shù)一樣
3、靜態(tài)方法調(diào)用一次就執(zhí)行一次
3、觸發(fā)類加載的幾種情況(靜態(tài)代碼塊)
1、創(chuàng)建對(duì)象實(shí)例時(shí)
2、當(dāng)創(chuàng)建子類對(duì)象實(shí)例時(shí),先加載父類,再加載子類
3、使用類的靜態(tài)成員(靜態(tài)屬性、靜態(tài)方法)
4、還有一種是反射( 我還沒(méi)學(xué)>_<...)
#代碼驗(yàn)證:
public class codeBlock02 {
public static void main(String[] args) {
new CC();
//1、創(chuàng)建對(duì)象實(shí)例時(shí)會(huì)觸發(fā)類加載,執(zhí)行靜態(tài)代碼塊。
//2、當(dāng)創(chuàng)建子類對(duì)象實(shí)例時(shí),當(dāng)前類CC向上的所有父類AA、BB類都會(huì)觸發(fā)類加載,從而執(zhí)行對(duì)應(yīng)類的靜態(tài)代碼塊
//System.out.println(CC.str);
//1、當(dāng)調(diào)用靜態(tài)成員(靜態(tài)方法和變量)時(shí),會(huì)觸發(fā)類加載,執(zhí)行靜態(tài)代碼塊內(nèi)容
}
}
class AA{
//普通代碼塊
{
System.out.println("普通代碼塊AA");
}
//靜態(tài)代碼塊
static {
System.out.println("靜態(tài)代碼塊AA");
}
}
class BB extends AA{
//普通代碼塊
{
System.out.println("普通代碼塊BB");
}
//靜態(tài)代碼塊
static {
System.out.println("靜態(tài)代碼塊BB");
}
}
class CC extends BB{
public static String str="這是靜態(tài)變量";
//普通代碼塊
{
System.out.println("普通代碼塊CC");
}
//靜態(tài)代碼塊
static {
System.out.println("靜態(tài)代碼塊CC");
}
public CC() {
System.out.println("這是CC的無(wú)參構(gòu)造器");
}
}
執(zhí)行new CC()輸出:
靜態(tài)代碼塊AA
靜態(tài)代碼塊BB
靜態(tài)代碼塊CC
普通代碼塊AA
普通代碼塊BB
普通代碼塊CC
這是CC的無(wú)參構(gòu)造器
執(zhí)行System.out.println(CC.str)輸出:
靜態(tài)代碼塊AA
靜態(tài)代碼塊BB
靜態(tài)代碼塊CC
這是靜態(tài)變量
說(shuō)明:上面結(jié)論是正確的!
4、創(chuàng)建一個(gè)類對(duì)象時(shí),類中靜態(tài)代碼塊、靜態(tài)變量、普通代碼塊、普通變量、構(gòu)造器的執(zhí)行順序是什么?
上代碼測(cè)試:
public class codeBlock02 {
public static void main(String[] args) {
AA aa = new AA();
}
}
class AA{
//普通變量
public int numb = getNumb();
//靜態(tài)類變量
public static String str=getStr();
//普通方法
public int getNumb(){
System.out.println("getNumb被執(zhí)行了");
return 10;
}
//靜態(tài)方法
public static String getStr(){
System.out.println("getStr()方法被執(zhí)行");
return "類變量";
}
//構(gòu)造器
public AA(){
super();//當(dāng)有父類時(shí)會(huì)執(zhí)行,沒(méi)有不執(zhí)行 <1>
<2>
System.out.println("這是AA的無(wú)參構(gòu)造器"); <3>
}
// 在<2>這里我感覺(jué)隱藏了調(diào)用普通代碼塊的引用代碼,因?yàn)槊縩ew一個(gè)對(duì)象時(shí),執(zhí)行順序都是: <1> ---> <2> ---> <3>;
//普通代碼塊
{ System.out.println("普通代碼塊AA");}
//靜態(tài)代碼塊
static {System.out.println("靜態(tài)代碼塊AA");}
}
//輸出:
// getStr()方法被執(zhí)行 ---》靜態(tài)類變量
// 靜態(tài)代碼塊AA ---》靜態(tài)代碼塊
// getNumb被執(zhí)行了 ---》普通類變量
// 普通代碼塊AA---》普通代碼塊
// 這是AA的無(wú)參構(gòu)造器 ---》構(gòu)造器
說(shuō)明什么?
1、先執(zhí)行靜態(tài)static的靜態(tài)代碼塊或靜態(tài)類變量,這兩優(yōu)先級(jí)一樣,執(zhí)行順序就看定義的位置誰(shuí)先誰(shuí)后
2、然后執(zhí)行普通代碼塊或普通類變量,這兩優(yōu)先級(jí)也一樣,執(zhí)行順序就看定義的位置誰(shuí)先誰(shuí)后
3、最后執(zhí)行構(gòu)造器
4、在構(gòu)造器中super()下面還隱藏了一段看不見(jiàn)的調(diào)用普通代碼塊的引用代碼
5、小練習(xí)(代碼塊)
#請(qǐng)問(wèn)以下代碼輸出什么?
public class codeBlock01 {
public static void main(String[] args) {
new B();
}
}
class A{
//靜態(tài)屬性
public static int number_A = getA1();
//靜態(tài)代碼塊
static { System.out.println("A類靜態(tài)代碼塊");}
//普通代碼塊
{ System.out.println("A類普通代碼塊"); }
//普通屬性
public int age_A = getA2();
//父類構(gòu)造方法
public A(){
System.out.println("打印A類構(gòu)造器");
}
public static int getA1(){
System.out.println("調(diào)用了getA1方法");
return 20;
}
public int getA2(){
System.out.println("調(diào)用了getA2方法");
return 31;
}
}
class B extends A{
//靜態(tài)代碼塊
static { System.out.println("B類的靜態(tài)代碼塊"); }
//靜態(tài)屬性
public static String getStringB1 = getB1();
//普通代碼塊
{ System.out.println("B類的普通代碼塊"); }
//普通屬性
public String getStringB2 = getB2();
//子類構(gòu)造方法
public B(){
super();
System.out.println("打印A類構(gòu)造器");
}
public static String getB1(){
System.out.println("調(diào)用了getB1方法");
return "B1";
}
public String getB2(){
System.out.println("調(diào)用了getB2方法");
return "B2";
}
}
輸出:
調(diào)用了getA1方法
A類靜態(tài)代碼塊
B類的靜態(tài)代碼塊
調(diào)用了getB1方法
A類普通代碼塊
調(diào)用了getA2方法
打印A類構(gòu)造器
B類的普通代碼塊
調(diào)用了getB2方法
打印A類構(gòu)造器
6、小練習(xí)
以下程序會(huì)輸出?
public class Test {
static String s = "琳";
static {
String s = "麗";
System.out.println(s); // <1> 靜態(tài)代碼塊最先執(zhí)行,輸出麗
}
public static void main(String[] args) {
new Son(s); //把"琳"傳進(jìn)去
}
}
class Father{
static int m=100;
public Father(){ m=999;} //父類的m=999,不改變靜態(tài)變量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éi)有賦值,所以m=0;
System.out.println(super.m); <6>輸出: 5
}
}
最終輸出:麗,父類10000,static block,"琳",0,5
5、單例模式
# 使用靜態(tài)方法和靜態(tài)屬性實(shí)現(xiàn)單例模式
# 單例模式特點(diǎn):一點(diǎn)創(chuàng)建了一個(gè)對(duì)象,之后就無(wú)法創(chuàng)建新對(duì)象,一個(gè)類只能有一個(gè)對(duì)象實(shí)例
#思路
1、限制創(chuàng)建對(duì)象
2、判斷是否已經(jīng)存在一個(gè)對(duì)象實(shí)例
代碼實(shí)現(xiàn):
public class SingletonMode {
public static void main(String[] args) {
//固定式
Singleton1 singleton1 = Singleton1.getSingleton();
Singleton1 singleton2 = Singleton1.getSingleton();
System.out.println(singleton1==singleton2);//返回true,說(shuō)明成功了
//靈活式
Singleton2 singleton3 = Singleton2.getSingleton("小花");
Singleton2 singleton4 = Singleton2.getSingleton("小梅");
System.out.println(singleton3==singleton4);//返回true,說(shuō)明成功了
/*
實(shí)現(xiàn)單例模式
思路:
1、限制創(chuàng)建新對(duì)象
2、判斷該類中是否已經(jīng)存在一個(gè)對(duì)象實(shí)例
*/
}
}
//實(shí)現(xiàn)一:固定式
class Singleton1{ //缺點(diǎn)就是:這個(gè)name參數(shù)值在編寫(xiě)時(shí)必須寫(xiě)死,那有沒(méi)有自己傳參值來(lái)new一個(gè)呢,看下一個(gè)
private String name;
private static Singleton1 singleton = new Singleton1("小琳");
//私有化構(gòu)造器
private Singleton1(String name){
super();
this.name =name;
}
//提供一個(gè)static方法獲取對(duì)象
public static Singleton1 getSingleton(){
return singleton;
}
}
//實(shí)現(xiàn)二:靈活式 可以自定義單例對(duì)象的數(shù)值
class Singleton2{
private String name;
private static Singleton2 Singleton;
//私有化構(gòu)造器
private Singleton2(String name){
super();
this.name = name;
}
//提供一個(gè)判斷對(duì)象是否存在且可以返回對(duì)象的方法
public static Singleton2 getSingleton(String name){
//判斷當(dāng)前是否存在對(duì)象
if (Singleton == null){
return Singleton = new Singleton2(name);
}
return Singleton;
}
}
小結(jié)(固定式和靈活式的區(qū)別):
對(duì)象創(chuàng)建時(shí)間上:
固定式是在類加載時(shí)就創(chuàng)建了該對(duì)象,而靈活式要調(diào)用才會(huì)創(chuàng)建對(duì)象
資源浪費(fèi)上:
固定式有可能創(chuàng)建后就沒(méi)有用過(guò)所以存在浪費(fèi)資源的可能,而靈活式就不會(huì)有這情況
靈活度上:
固定式是在編寫(xiě)代碼時(shí)對(duì)象就確定了,后續(xù)不能改變,而靈活式可以在第一次創(chuàng)建對(duì)象時(shí)選擇創(chuàng)建的對(duì)象值
線程安全上:
固定式是線程安全的,靈活式是線程不安全的!
6、抽象類
6.1、抽象類的介紹
1、抽象類定義
訪問(wèn)修飾符 abstract 類名{ }//沒(méi)有類實(shí)現(xiàn)
2、抽象方法定義
訪問(wèn)修飾符 abstract 方法名(參數(shù)列表)//沒(méi)有方法體
3、抽象類的價(jià)值更多的體現(xiàn)在設(shè)計(jì)層面,設(shè)計(jì)者設(shè)計(jì)好抽象類讓子類繼承并實(shí)現(xiàn)抽象類的功能,更多的是充當(dāng)一個(gè)模板的作用,應(yīng)用場(chǎng)景更多體現(xiàn)在設(shè)計(jì)模式和框架上!
6.2、抽象類的特點(diǎn)
1、抽象類不能被實(shí)例化創(chuàng)建
2、抽象類中,可以有抽象方法,也可以沒(méi)有抽象方法
3、一個(gè)方法一旦聲明了成抽象方法,該方法的類也必須聲明為抽象類!總結(jié):抽象類不一定有抽象方法,但有抽象方法的類一定是抽象類
4、abstract只能修飾類和方法,不能修飾類屬性(只有抽象類和方法之說(shuō),沒(méi)有抽象屬性之說(shuō))
5、普通類里可以有的抽象類都可以有(如靜態(tài)方法、靜態(tài)屬性、靜態(tài)代碼塊、普通代碼塊),普通類里沒(méi)有的抽象方法抽象類有!
6、抽象方法沒(méi)有方法體
7、如果一個(gè)類繼承了抽象類,那就必須實(shí)現(xiàn)該抽象類的所有抽象方法,除非把自己也聲明為抽象類!
8、抽象方法不能使用private、final、static修飾。
#因?yàn)槌橄蠓椒ǘx后必須實(shí)現(xiàn),而重寫(xiě)也是一種實(shí)現(xiàn)的方式,private私有化不能被重寫(xiě),final不能被繼承也就無(wú)法重寫(xiě),static靜態(tài)化方法只跟類相關(guān),沒(méi)有對(duì)象去實(shí)現(xiàn)這個(gè)抽象方法,重寫(xiě)是跟對(duì)象相關(guān)的!
6.3、多態(tài)在抽象類的體現(xiàn)
1、多態(tài)參數(shù):
[抽象父類的引用也可以指向繼承它的所有子類對(duì)象,所以可以使用抽象父類的引用作為形參去接收傳遞過(guò)來(lái)的所有子類對(duì)象參數(shù)] 什么意思?看下面代碼
代碼:
----------------------------------------------------------------------------------------
/* 驗(yàn)證:
多態(tài)參數(shù):接收子類對(duì)象
多態(tài)數(shù)組:存儲(chǔ)各個(gè)子類對(duì)象
*/
//抽象父類-寵物類
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);
}
}
//測(cè)試類
public class Abstract01 {
public static void main(String[] args) {
Pets cat = new petsCat("加菲貓");
Pets dog = new petsDog("旺財(cái)");
//抽象父類引用Pets可以作為形參接收petsCat("加菲貓")和petsDog("旺財(cái)")
// 這兩個(gè)子類對(duì)象實(shí)例,就證明了抽象類也具有多態(tài)參數(shù)的特性
}
}
輸出:
加菲貓
旺財(cái)
2、多態(tài)數(shù)組:
[抽象父類也可以存放所有繼承的子類對(duì)象]什么意思?看下面代碼
代碼:
/* 驗(yàn)證:
多態(tài)參數(shù):接收子類對(duì)象
多態(tài)數(shù)組:存儲(chǔ)各個(gè)子類對(duì)象
*/
//抽象父類-寵物類
abstract class Pets{
private String name;
public Pets(String name) {
super();
this.name = name;
System.out.println(name);
}
public String getName(){
return name;
}
//定義一個(gè)公有的抽象方法play(),玩
public abstract void play();
}
//寵物狗類
class petsDog extends Pets{
public petsDog(String name){
super(name);
}
//重寫(xiě)實(shí)現(xiàn)play()方法
@Override
public void play() {
System.out.println(getName()+"可以拿來(lái)擼");
}
//狗特有行為:看大門(mén)
public void guardHome(){
System.out.println(getName()+"會(huì)幫主人看家");
}
}
//寵物貓類
class petsCat extends Pets{
public petsCat(String name){
super(name);
}
@Override
public void play(){
System.out.println(getName()+"可以拿來(lái)擼");
}
//貓?zhí)赜行袨?抓老鼠
public void captureMouse(){
System.out.println(getName()+"會(huì)抓老鼠");
}
}
//測(cè)試類
public class Abstract01 {
public static void main(String[] args) {
//在這里定義一個(gè)抽象父類Pets數(shù)組,去接收不同的子類對(duì)象
Pets[] pets = new Pets[]{new petsCat("加菲貓"),new petsDog("旺財(cái)"),};
//抽象父類引用pets去接收并存儲(chǔ) new petsCat("加菲貓") 和 new petsDog("旺財(cái)")子類對(duì)象,運(yùn)行時(shí)會(huì)進(jìn)行動(dòng)態(tài)綁定!
new Abstract01().getObject(pets);
}
//定義一個(gè)判斷子類對(duì)象類型的方法,并打印出對(duì)象對(duì)應(yīng)的特有方法
public void getObject(Pets[] abs){
//使用instanceof進(jìn)行子類對(duì)象類型判斷
for (Pets p:abs){
if (p instanceof petsCat){
((petsCat)p).captureMouse();
}else { //因?yàn)槌橄蟾割悷o(wú)法被實(shí)例化,所以不可能會(huì)有父類的對(duì)象出現(xiàn),對(duì)象只可能兩種類型
((petsDog) p).guardHome();
}
}
}
}
6.4、抽象類體現(xiàn)了模板設(shè)計(jì)模式
1、抽象充當(dāng)了多個(gè)子類的通用模板,子類可以在繼承抽象類的基礎(chǔ)上進(jìn)行擴(kuò)展,但子類中還是保留一定的抽象父類的行為方法
2、什么是模板設(shè)計(jì)模式?
#當(dāng)有一部分功能是確定的,一部分功能是不確定的,可以用模板設(shè)計(jì)模式設(shè)計(jì)一個(gè)抽象父類,里面定義一個(gè)抽象方法,這個(gè)抽象方法做不確定功能,確定的功能在抽象父類寫(xiě)一個(gè)方法實(shí)現(xiàn)好;然后讓子類繼承并實(shí)現(xiàn)這個(gè)功能不確定的抽象方法
上代碼演示:
3、需求:我想要一個(gè)抽象類,用于計(jì)算一個(gè)方法執(zhí)行的時(shí)間(什么方法不確定,但是用于計(jì)算時(shí)間的方法確定)
//抽象計(jì)算類
//抽象計(jì)算類
abstract class Calculation{
//抽象方法
abstract public void Method();
//計(jì)算耗時(shí)的方法
public void consumeTime(){
long start_time = System.currentTimeMillis();
Method();
long end_time = System.currentTimeMillis();
System.out.println("該方法耗時(shí)為:"+(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");
}
}
}
//測(cè)試類
public class Template {
public static void main(String[] args) {
//實(shí)現(xiàn)一:
//new Son1().consumeTime();
//son1先找自己consumeTime方法,沒(méi)有就找父類consumeTime方法,
// 父類consumeTime根據(jù)動(dòng)態(tài)綁定調(diào)用自己的Method方法
//new Son2().consumeTime();
//new Son3().consumeTime();
//實(shí)現(xiàn)二:
Template.test(new Son1()); //調(diào)用靜態(tài)方法:類名.方法名
new Template().test(new Son2());//調(diào)用靜態(tài)方法:對(duì)象.方法名
test(new Son3()); //因?yàn)樵摲椒ㄔ谕愔?,直接方法? }
public static void test(Calculation c){
c.consumeTime();
}
}
輸出:
該方法耗時(shí)為:12139
該方法耗時(shí)為:3
該方法耗時(shí)為:2
//說(shuō)明字符串拼接操作效率:StringBuilder > StringBuffer > String;
7、接口
1、接口介紹:
就是把一些沒(méi)有實(shí)現(xiàn)的方法通過(guò)接口封裝到一起,當(dāng)哪個(gè)類需要使用到接口的方法的時(shí)候,該類根據(jù)需求再具體去實(shí)現(xiàn)接口里的方法
接口定義格式:
interface 接口名{
屬性
抽象方法(接口里的方法默認(rèn)是abstract抽象的)
在jdk1.8后,接口里可以有 靜態(tài)方法 和 默認(rèn)方法的實(shí)現(xiàn)
}
實(shí)現(xiàn)接口格式:
class 類名 implements 接口名{
//可以有自己的屬性和方法,但是必須實(shí)現(xiàn)(重寫(xiě))接口的所有方法,可以跟接口隔離原則去優(yōu)化
自己屬性
自己方法
必須實(shí)現(xiàn)接口的抽象方法
}
#栗子:
//接口
interface interfaceDemo{
//接口里的屬性必須定義時(shí)給定初始值
public String str="111";
//等同public static final String str = "111"
//靜態(tài)方法
public static void print01(){
System.out.println("靜態(tài)方法");
}
//默認(rèn)方法
public default void pint02(){
System.out.println("默認(rèn)方法");
}
//抽象方法
void absMethod1();
void absMethod2();
}
//測(cè)試類
public class Interface02 {
public static void main(String[] args) {
//接口里的靜態(tài)方法直接用接口名調(diào)用,跟類的靜態(tài)方法調(diào)用一樣
interfaceDemo.print01();
//默認(rèn)方法接口自己無(wú)法使用接口名調(diào)用
//interfaceDemo.print02();
}
}
//普通類
class Ordinary implements interfaceDemo{
//實(shí)現(xiàn)一個(gè)抽象方法1還是報(bào)錯(cuò),接著實(shí)現(xiàn)方法2報(bào)錯(cuò),實(shí)現(xiàn)默認(rèn)方法后不報(bào)錯(cuò),說(shuō)明要全部實(shí)現(xiàn)(包含默認(rèn)方法)
@Override
void pint02() { //一旦我把print2方法的改為默認(rèn),就報(bào)錯(cuò),而根據(jù)實(shí)現(xiàn)接口的子類方法訪問(wèn)權(quán)限必須 >= 接口抽象方法的訪問(wèn)權(quán)限,
//那就證明接口的抽象方法的訪問(wèn)權(quán)限默認(rèn)是public
}
@Override
public void absMethod1() {
}
@Override
public void absMethod2() {
}
}
//抽象類
abstract class absClass implements interfaceDemo{
//不報(bào)錯(cuò),說(shuō)明抽象類可以不用實(shí)現(xiàn)接口的抽象方法
}
//測(cè)試類
public class Interface02 {
public static void main(String[] args) {
//接口里的靜態(tài)方法直接用接口名調(diào)用,跟類的靜態(tài)方法調(diào)用一樣
interfaceDemo.print01();
//默認(rèn)方法接口自己無(wú)法使用接口名調(diào)用,只能給實(shí)現(xiàn)了接口的子類調(diào)用
//interfaceDemo.print02();
}
}
小細(xì)節(jié):
1、在jdk1.8后,接口里可以有 靜態(tài)方法 和 默認(rèn)方法的實(shí)現(xiàn) 及定義接口自己屬性
2、接口里的屬性必須定義時(shí)給定初始值,等同于常量屬性
3、接口里的靜態(tài)方法直接用接口名調(diào)用,跟類的靜態(tài)方法調(diào)用一樣
4、默認(rèn)方法接口自己無(wú)法使用接口名調(diào)用,只能給實(shí)現(xiàn)了接口的子類調(diào)用
5、實(shí)現(xiàn)接口的子類方法權(quán)限 >= 接口的抽象方法訪問(wèn)權(quán)限
6、抽象類可以實(shí)現(xiàn)接口,但可以實(shí)現(xiàn)接口的抽象方法
7、接口可以繼承別的接口,但是不能繼承普通類,當(dāng)然也包含抽象類
//注意:
1、當(dāng)多個(gè)接口里如果有重名的抽象方法,不會(huì)影響使用,如果是重名的常量屬性時(shí),調(diào)用時(shí)使用接口名.屬性名區(qū)分
7.2、繼承與接口在應(yīng)用上有什么區(qū)別?
1、接口其實(shí)是對(duì)java單繼承缺點(diǎn)的一種彌補(bǔ)和擴(kuò)展,讓子類有更豐富的方法和屬性,而這些屬性來(lái)源于子類實(shí)現(xiàn)接口里的抽象方法!
#栗子
package P3;
//猴子父類
class Monkey{
private String name;
public Monkey(String name){
this.name = name;
}
public String getName(){
return name;
}
//爬樹(shù)
public void climbTree(){
System.out.println(getName()+"會(huì)爬樹(shù)");
}
}
//想要給悟空加技能,會(huì)上天飛,會(huì)下海潛,使用實(shí)現(xiàn)接口的抽象方法
interface FlightClass{
public void flight();//會(huì)飛行
}
interface DivingClass{
public void diving();//會(huì)潛水
}
//孫悟空類
class sunMonkey extends Monkey implements DivingClass,FlightClass{
public sunMonkey(String name) {
super(name);
}
@Override
public void climbTree() {
super.climbTree();
}
//實(shí)現(xiàn)飛行和潛水方法
@Override
public void diving(){
System.out.println(getName()+"會(huì)潛水");
}
@Override
public void flight(){
System.out.println(getName()+"會(huì)飛行");
}
}
//測(cè)試類
public class Relation {
public static void main(String[] args) {
new sunMonkey("孫悟空").diving();
new sunMonkey("孫悟空").flight();
new sunMonkey("孫悟空").climbTree();
//接口多態(tài),跟普通類多態(tài)一樣具有多態(tài)參數(shù)和多態(tài)數(shù)組特性
test1(new sunMonkey("孫大圣"));
test2(new sunMonkey("孫大圣"));
test3(new sunMonkey("孫大圣"));
}
//在別的類的方法中去接收父類對(duì)象和實(shí)現(xiàn)接口方法的對(duì)象,這就是多態(tài)
public static void test1(Monkey monkey){
monkey.climbTree();
}
public static void test2(DivingClass divingClass){
divingClass.diving();
}
public static void test3(FlightClass flightClass){
flightClass.flight();
}
}
輸出:
孫悟空會(huì)潛水
孫悟空會(huì)飛行
孫悟空會(huì)爬樹(shù)
孫大圣會(huì)爬樹(shù)
孫大圣會(huì)潛水
孫大圣會(huì)飛行
7.3、接口的多態(tài)性
# 接口的多態(tài)跟類的多態(tài)差不多,也有多態(tài)參數(shù)和多態(tài)數(shù)組的特性
1、多態(tài)參數(shù) 與 多態(tài)數(shù)組
代碼舉栗:
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;}
//游戲運(yùn)行
public void runGame(IGame iGame){
//這里使用接口引用去接收實(shí)現(xiàn)該接口的類的對(duì)象,體現(xiàn)了接口的多態(tài)參數(shù)
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()+"加載完成,可以開(kāi)始游戲啦....");
}
@Override
public void end() {
System.out.println(getGameName()+"正在關(guān)閉游戲....");
}
//獨(dú)特方法
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()+"加載完成,可以開(kāi)始游戲啦....");
}
@Override
public void end() {
System.out.println(getGameName()+"正在關(guān)閉游戲....");
}
//獨(dú)特方法
public void cfObject(){
System.out.println(getGameName()+"是多人槍?xiě)?zhàn)游戲");
}
}
//測(cè)試類
public class Relation {
public static void main(String[] args) {
//接口多態(tài)參數(shù)的體現(xiàn)
Game game = new Game("LOL");
game.runGame(new LOL("LOL"));
game.runGame(new CF("CF"));
System.out.println("----------------------");
//接口多態(tài)數(shù)組的體現(xiàn)
IGame[] iGames = new IGame[]{new LOL("英雄聯(lián)盟"),new CF("穿越火線")};
for (IGame i: iGames) {
if (i instanceof LOL){
//如果想調(diào)用子類對(duì)象的獨(dú)特方法,可以使用instanceof進(jìn)行對(duì)象類型判斷,
((LOL) i).lolObject();
}else { ((CF)i).cfObject();}
}
}
}
輸出:
LOL正在加載....
LOL加載完成,可以開(kāi)始游戲啦....
LOL正在關(guān)閉游戲....
CF正在加載....
CF加載完成,可以開(kāi)始游戲啦....
CF正在關(guān)閉游戲....
----------------------
英雄聯(lián)盟是5v5推塔游戲
穿越火線是多人槍?xiě)?zhàn)游戲
2、接口存在多態(tài)傳遞特性
代碼舉栗:
package p4;
import com.sun.xml.internal.ws.api.model.wsdl.WSDLOutput;
//測(cè)試類
public class Interface{
public static void main(String[] args) {
Son son = new Son();
//這是實(shí)現(xiàn)了子接口Ison的子類Son對(duì)象
ISon iSon = son;
//這里可以把son對(duì)象賦值給子接口ISon的引用,說(shuō)明子接口引用是可以指向并接收Son類對(duì)象的
IFather iFather = son;
//這里可以把son對(duì)象賦值給父接口IFather的引用,說(shuō)明父接口引用是可以指向并接收Son類對(duì)象的
Father father = son;
//這里可以把son對(duì)象賦值給父類Father的引用,說(shuō)明父類引用是可以指向并接收Son類對(duì)象的
}
}
//父接口
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()方法");
}
}
小結(jié):
1、當(dāng)一個(gè)接口B繼承接口A,而子類b繼承父類a,而b實(shí)現(xiàn)接口B方法時(shí),b類對(duì)象可以傳遞給接口B的所有父接口的接口引用,這就是接口的多層傳遞
3、多態(tài)參數(shù)與多態(tài)數(shù)組在使用上的區(qū)別?
1、多態(tài)數(shù)組在程序運(yùn)行時(shí),所有繼承的子類對(duì)象或?qū)崿F(xiàn)接口抽象方法的類對(duì)象,可以以數(shù)組的方式同時(shí)傳遞多個(gè)對(duì)象給父類引用
2、而參數(shù)多態(tài)只能同時(shí)傳遞一種類對(duì)象
3、所以當(dāng)需要使用多個(gè)不同類對(duì)象時(shí),使用多態(tài)數(shù)組;反之,使用多態(tài)參數(shù)就可以了
4、小練習(xí)
//課后小練習(xí):以下程序輸出什么?如有錯(cuò)誤,請(qǐng)修改
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();
}
}
解:
錯(cuò)誤:調(diào)用a會(huì)有歧義。如果想調(diào)用接口的a,使用接口名A.a ; 如果想用B類的a,使用super.a
8、內(nèi)部類
8.1 、內(nèi)部類介紹
1、內(nèi)部類介紹:
#一個(gè)類中有嵌入一個(gè)完整的類結(jié)構(gòu),這個(gè)嵌套的類稱為內(nèi)部類
內(nèi)部類特點(diǎn):
1、內(nèi)部類可以直接訪問(wèn)外層類的私有屬性
2、內(nèi)部類的作用域在它的外部類中
3、本質(zhì)上其實(shí)也是外部類的一個(gè)成員,其他都一樣
內(nèi)部類定義格式:
class Outer{ //外部類
class Inner{//內(nèi)部類
public void Method1(){
class Inner01{} //方法里的內(nèi)部類
}
}
}
調(diào)用內(nèi)部類格式:
new 外部類().new 內(nèi)部類().內(nèi)部類方法名或?qū)傩?
代碼舉栗:
//外部類
class A{
private int a = 1;
private void A(){
System.out.println("這是外層類的A方法");
}
//內(nèi)部類
class Inner{
public void print(){
System.out.println(a);
}
}
public static void main(String[] args) {
new A().new Inner().print();
}
}
輸出:
1
2、內(nèi)部類的分類:
*在外部類的方法中
1、局部?jī)?nèi)部類(有類名)
2、匿名內(nèi)部類(沒(méi)有類名,這是重點(diǎn),常用)
*類似外部類的成員類
1、成員內(nèi)部類(沒(méi)用static修飾)
2、靜態(tài)內(nèi)部類(用static修飾)
8.2、局部?jī)?nèi)部類
1、局部?jī)?nèi)部類的介紹:
訪問(wèn)權(quán)限: 可以訪問(wèn)外部類所有成員和自己的成員
位置: 定義在外部類的方法或代碼塊中
修飾符: 只能使用final常量修飾符,因?yàn)樗淖饔糜騼H限于局部方法中,局部變量不能用修飾符,但final可以
作用域: 僅在外部類的方法或代碼塊中
#使用局部?jī)?nèi)部類舉栗:
//外部類
class A{
private int a = 1;
public void f1(){
//外部類訪問(wèn)局部?jī)?nèi)部類
};
//外部類
class C1{ public void m1(){}};
private void print(){
class LocalInnerClass{
public void LocalInnerMethod(){
System.out.println("這是局部?jī)?nèi)部類的方法");
//局部?jī)?nèi)部類-->訪問(wèn)-->外部類成員-->直接訪問(wèn)
f1();
System.out.println(a);
}
}
//如果想在外部類調(diào)用局部?jī)?nèi)部類里的方法,這里得先這樣
new LocalInnerClass().LocalInnerMethod();
}
public static void main(String[] args) {
new A().print();
}
}
8.3、匿名內(nèi)部類
1、匿名內(nèi)部類的介紹
方式一(使用接口名new)、
//接口
interface Interface1{
public void print();
}
class Anonymous {
public void Method() {
/*這是匿名內(nèi)部類方式一、
* 解讀:
*1、編譯類型是接口Interface1,
*2、運(yùn)行類型是:
* new Interface1() {
@Override
public void print() {
System.out.println("這是匿名內(nèi)部類");
}
};
* 3、匿名內(nèi)部類是一個(gè)對(duì)象,也相當(dāng)于實(shí)現(xiàn)接口Interface1的無(wú)名子類
* */
Interface1 i = new Interface1() {
@Override
public void print() {
System.out.println("這是匿名內(nèi)部類");
}
};
i.print();
}
//測(cè)試
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() {
/*這是匿名內(nèi)部類方式一、
* 解讀:
*1、可以理解為:相當(dāng)于接口Interface1的子類的實(shí)例對(duì)象
*2、編譯類型是接口Interface1,
*3、運(yùn)行類型是: class p4.Anonymous$1 表示Anonymous編號(hào)為1的內(nèi)部類
Interface1() {
@Override
public void print() {
System.out.println("這是匿名內(nèi)部類");
}
};
* 3、匿名內(nèi)部類是一個(gè)對(duì)象,也相當(dāng)于實(shí)現(xiàn)接口Interface1的無(wú)名子類
* */
Interface1 i = new Interface1() {
@Override
public void print() {
System.out.println("這是匿名內(nèi)部類方式一");
}
}; //4、匿名內(nèi)部類尾部記得加;
i.print();
System.out.println(i.getClass());
}
public void Method1(){
/*匿名內(nèi)部類方式二
* 1、可以理解為:相當(dāng)于類FatherClass的子類的實(shí)例對(duì)象
* 2、編譯類型是FatherClass類
* 3、運(yùn)行類型是: class p4.Anonymous$2 表示Anonymous編號(hào)為2的內(nèi)部類
FatherClass() {
@Override
public void test() {
System.out.println("這是匿名內(nèi)部類方式二");
}
};
* */
FatherClass father = new FatherClass() {
@Override
public void test() {
System.out.println("這是匿名內(nèi)部類方式二");
}
};father.test();
System.out.println(father.getClass());
}
//測(cè)試
public static void main(String[] args) {
new Anonymous().Method();//匿名內(nèi)部類方式一
new Anonymous().Method1();//匿名內(nèi)部類方式二
}
}
輸出:
這是匿名內(nèi)部類方式一
class p4.Anonymous$1
這是匿名內(nèi)部類方式二
class p4.Anonymous$2
2、匿名內(nèi)部類的調(diào)用特點(diǎn):
1、通過(guò)對(duì)象本身調(diào)用內(nèi)部類的方法
new FatherClass() {
@Override
public void test() {
System.out.println("這是匿名內(nèi)部類方式二");
}
}.test();
2、通過(guò)對(duì)象引用調(diào)用本身內(nèi)部的方法
FatherClass father = new FatherClass() {
@Override
public void test() {
System.out.println("這是匿名內(nèi)部類方式二");
}
};father.test();
3、匿名內(nèi)部類的應(yīng)用場(chǎng)景
1、當(dāng)作實(shí)參直接傳遞
代碼舉栗:
public class Anonymous01 {
public static void main(String[] args) {
Kitchen kitchen = new Kitchen();
/**
* 1、匿名內(nèi)部類只執(zhí)行一次,適合作參數(shù)進(jìn)行傳遞
* */
kitchen.rinseFood(new Soup(){
public void cooking(){
System.out.println("午餐吃陽(yáng)澄湖大閘蟹");
}
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();
}
}
輸出:
午餐吃陽(yáng)澄湖大閘蟹
加三鮮湯
午餐吃糖醋排骨
加排骨蘿卜湯
4、如何使用多態(tài)機(jī)制把局部?jī)?nèi)部類接到外部類使用
##### 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()返回對(duì)象類型是其實(shí)是實(shí)現(xiàn)接口InnerH3類的對(duì)象類型,
* 屬于外部類Frock的成員,所以不需要向下轉(zhuǎn)型
* */
Frock frock = new Frock();
Inner inner = frock.h2();
inner.show();
System.out.println(frock.getClass());
System.out.println(inner.getClass());
}
}
//1、使用接口把內(nèi)部類InnerH3接出來(lái)
interface Inner{ public void show();}
//使用子類把InnerH3接出來(lái)
//外部類Frock
class Frock {
int a1 = 20;
//局部?jī)?nèi)部類
public Inner h2(){
class InnerH3 implements Inner{
int a1 = 10;
public void show(){
System.out.println("------------------");
//內(nèi)部類成員與外部類成員重名時(shí),在內(nèi)部類調(diào)用屬性遵循就近原則
System.out.println("局部?jī)?nèi)部類-a1:"+a1);
//調(diào)用外部類成員名使用必須 外部類名.this.重名屬性名,this.重名屬性名都不行
System.out.println("外部類屬性-a1:"+Frock.this.a1);
}
}
InnerH3 innerH3 = new InnerH3();
innerH3.show();
return innerH3;
}
}
輸出:
------------------
局部?jī)?nèi)部類-a1:10
外部類屬性-a1:20
------------------
局部?jī)?nèi)部類-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對(duì)象類型是class p5.Frock1,
* 而inner1類型是 class p5.Frock1$1InnerH3,所以需要向下轉(zhuǎn)型
* 2、 frock1.h2()返回對(duì)象是抽象類的子類,轉(zhuǎn)成抽象類類型Inner1時(shí),所以需要向下轉(zhuǎn)型
* */
Frock1 frock1 = new Frock1();
Inner1 inner1= (Inner1) frock1.h2();
inner1.show();
System.out.println(frock1.getClass());
System.out.println(inner1.getClass());
}
}
//1、使用接口把內(nèi)部類InnerH3接出來(lái)
//interface Inner{ public void show();}
//2、使用抽象類把InnerH3接出來(lái)
abstract class Inner1{ //
/**第一步,建一個(gè)抽象類和抽象方法,
* 給h2()方法提供一個(gè)接受內(nèi)部類對(duì)象
* */
public void show(){}
}
//外部類Frock
class Frock1 {
int a1 = 20;
//局部?jī)?nèi)部類
public Inner1 h2(){
/**
* 第二步,讓內(nèi)部類繼承抽象類,
* 然后返回內(nèi)部類對(duì)象,讓抽象類接收
* */
class InnerH3 extends Inner1{
int a1 = 10;
public void show(){
System.out.println("------------------");
//內(nèi)部類成員與外部類成員重名時(shí),在內(nèi)部類調(diào)用屬性遵循就近原則
System.out.println("局部?jī)?nèi)部類-a1:"+a1);
//調(diào)用外部類成員名使用必須 外部類名.this.重名屬性名,this.重名屬性名都不行
System.out.println("外部類屬性-a1:"+Frock1.this.a1);
}
}
InnerH3 innerH3 = new InnerH3();
innerH3.show();
return innerH3;
}
}
8.4、 成員內(nèi)部類
1、成員內(nèi)部類介紹:
1、顧名思義,就是定義在外部類的成員位置(除方法中外),沒(méi)有static修飾,可以直接訪問(wèn)外部類的所有成員,包括私有屬性!
2、它可以使用任何訪問(wèn)修飾符,因?yàn)樗牡匚粚儆诔蓡T而不是局部
3、如果成員內(nèi)部類的成員與外部類的成員有重名的,那么在內(nèi)部類調(diào)用內(nèi)部類成員,直接調(diào)用;在內(nèi)部類調(diào)用外部類重名成員時(shí),使用 外部類名.this.重名成員名
格式長(zhǎng)這樣:
class OtherClass{
private String str="老黃牛"
class InnerClass{
public void show(){
System.out.println("向"+str+"學(xué)習(xí)")
}
}
}
代碼舉栗(成員內(nèi)部類):
package p5;
public class Test01 {
public static void main(String[] args) {
//調(diào)用成員內(nèi)部類方式一、
OtherClass otherClass = new OtherClass();
otherClass.new InnerClass().show();
//方式二、
otherClass.Method().show();
}
}
class OtherClass{
private String str="老黃牛";
private void PMethod(){
System.out.println("我是私有方法");
}
//成員內(nèi)部類
class InnerClass{
public InnerClass show(){
//1、成員內(nèi)部類訪問(wèn)外部類,直接訪問(wèn)
System.out.println("向"+str+"學(xué)習(xí)");
PMethod();
return null;
}
}
public InnerClass Method(){
//2、外部類訪問(wèn)成員內(nèi)部類的成員,先創(chuàng)建內(nèi)部類對(duì)象.成員
return new InnerClass();
}
}
2、如果內(nèi)部成員類被私有化了,外部其他類還是想訪問(wèn)該私有成員類中的方法呢,怎么辦呢?還是用接口接出來(lái)
代碼舉栗:
方式一、接口
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();
}
//成員內(nèi)部類
private class InnerClass implements I{
public void show(){
//1、成員內(nèi)部類訪問(wèn)外部類,直接訪問(wèn)
System.out.println("向"+str+"學(xué)習(xí)");
PMethod();
}
}
}
輸出:
向老黃牛學(xué)習(xí)
-------------------------------------------------------------------------------
方式二、抽象類
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();
}
//成員內(nèi)部類
private class InnerClass extends ABS{
public void show(){
//1、成員內(nèi)部類訪問(wèn)外部類,直接訪問(wèn)
System.out.println("向"+str+"學(xué)習(xí)");
PMethod();
}
}
}
輸出:
向老黃牛學(xué)習(xí)
3、如果成員內(nèi)部類的成員與外部類的成員有重名的,那么調(diào)用在內(nèi)部類調(diào)用外部類成員,直接調(diào)用; 在外部類調(diào)用內(nèi)部類成員時(shí),使用 外部類名.this.內(nèi)部類成員名
8.5、 靜態(tài)內(nèi)部類
1、靜態(tài)內(nèi)部類的介紹
1、靜態(tài)內(nèi)部類 = 使用static修飾內(nèi)部類
2、跟成員內(nèi)部類其他的相同,唯一區(qū)別就是:靜態(tài)內(nèi)部類只能訪問(wèn)外部類的靜態(tài)成員
2、靜態(tài)內(nèi)部類的使用方式:
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);
}
}
//在外部其他類,使用靜態(tài)內(nèi)部類的Method()方法
//方式一、使用靜態(tài)方法直接調(diào)用靜態(tài)方法
public static void Method01(){
new InnerClass02().Method();
}
//方式二、使用該方法返回一個(gè)靜態(tài)內(nèi)部類對(duì)象
public InnerClass02 Method02(){
return new InnerClass02();
}
}
3、當(dāng)靜態(tài)內(nèi)部類成員與外部類成員出現(xiàn)重名時(shí),在內(nèi)部類調(diào)用內(nèi)部類重名的成員時(shí),直接調(diào)用; 在靜態(tài)內(nèi)部調(diào)用外部類靜態(tài)重名成員,使用外部類名.重名成員名
#這里就不寫(xiě)代碼舉栗了
9、枚舉
9.1、枚舉的介紹
1、枚舉的介紹:
1、枚舉是一種特殊的類,用于表示一組常量,比如性別只有男和女,一天只有24小時(shí)等常識(shí)常量
2、使用關(guān)鍵字euum來(lái)定義,常量名使用大寫(xiě),各常量之間使用,分割。 如eunum Sex{ MAN,WOWAN; }
2、枚舉的實(shí)現(xiàn)方式:
1) 方式一、自定義枚舉
public class Custom {
public static void main(String[] args) {
//類似調(diào)用靜態(tài)屬性調(diào)用,類名.屬性名
System.out.println(Season.spring);
System.out.println(Season.summer);
System.out.println(Season.autumn);
System.out.println(Season.winter);
}
}
//自定義枚舉: 季節(jié)類
class Season{
private String name;
private String description;
//把構(gòu)造器私有化,不給外部new新的對(duì)象
private Season(String name, String description) {
this.name = name;
this.description = description;
}
//外部只能讀取不能修改
public String getName() {
return name;
}
public String getDescription() {
return description;
}
//使用static + final是為了優(yōu)化底層(不會(huì)觸發(fā)類加載,但是這里new對(duì)象還是會(huì)類加載)
public static final Season spring = new Season("春季","溫暖");
public static final Season summer = new Season("夏季","炎熱");
public static final Season autumn = new Season("秋季","肅殺");
public static final Season winter = new Season("冬季","寒冷");
@Override
public String toString() {
return "季節(jié):" + name + " 特點(diǎn):"+ description;
}
}
自定義枚舉注意點(diǎn):
1、構(gòu)造器私有化
2、本類內(nèi)部使用 static+final 創(chuàng)建一組對(duì)象,對(duì)外暴露對(duì)象
3、只提供get方法,不提供set方法
2)方式二、使用enum關(guān)鍵字
package p5;
public class Custom {
public static void main(String[] args) {
//類似調(diào)用靜態(tài)屬性調(diào)用,類名.屬性名
System.out.println(Season1.SPRING);
System.out.println(Season1.SUMMER);
System.out.println(Season1.AUTUMN);
System.out.println(Season1.WINTER);
}
}
//使用enum關(guān)鍵字實(shí)現(xiàn)枚舉
enum Season1 {
//這個(gè)必須寫(xiě)到這個(gè)enum類的首行
//使用enum關(guān)鍵字,默認(rèn)會(huì)讓Season1繼承Enumeration這個(gè)類
SPRING("春季", "溫暖"),
SUMMER("夏季", "炎熱"),
AUTUMN("秋季", "肅殺"),
WINTER("冬季", "寒冷");
private String name;
private String description;
private Season1(String name, String description) {
this.name = name;
this.description = description;
}
public String getName() {
return name;
}
public String getDescription() {
return description;
}
@Override
public String toString() {
return "季節(jié):" + name + " 特點(diǎn):"+ description;
}
}
小結(jié)(用enum關(guān)鍵字實(shí)現(xiàn)枚舉注意細(xì)節(jié))
1、枚舉常量必須放在枚舉類首行
2、當(dāng)有多個(gè)枚舉對(duì)象時(shí),使用,隔開(kāi),用;結(jié)束
3、public static final Season1 枚舉對(duì)象 = new Seson1("春天","溫暖"); 等同于 SPRING("春天","溫暖"),
9.2、枚舉的常用方法
1、toString: Enum類已重寫(xiě)過(guò),返回的是當(dāng)前對(duì)象名(大寫(xiě)),子類可重寫(xiě)該方法,用于返回自定義的對(duì)象屬性信息
2、name: 返回當(dāng)前對(duì)象名(常量名),子欸不能重寫(xiě)
3、ordinal: 返回當(dāng)前對(duì)象的位置編號(hào),默認(rèn)從0開(kāi)始
4、values: 返回當(dāng)前枚舉類的所有常量
5、valueOf; 將字符串轉(zhuǎn)成枚舉對(duì)象,要求字符串必須為已存在的常量名,否則報(bào)異常
6、compareTo: 比較兩個(gè)枚舉常量的位置編號(hào),
代碼舉栗:
package p5;
import com.sun.scenario.effect.impl.sw.sse.SSEBlend_SRC_OUTPeer;
public class Custom {
public static void main(String[] args) {
Season1 autumn = Season1.AUTUMN;
System.out.println(autumn);//這里已重寫(xiě)過(guò)
System.out.println(autumn.name());//返回大寫(xiě)的autumn對(duì)象名:AUTUMN
System.out.println(autumn.ordinal());//返回該對(duì)象的編號(hào)(理解下標(biāo)也行),用于
System.out.println(Season1.values());//返回該枚舉中所有對(duì)象,是一個(gè)數(shù)組類型,可迭代
Season1[] season1s = (Season1.values());
for (Season1 s:season1s){
System.out.println(s);
}
System.out.println(Season1.valueOf("SPRING"));//判斷該枚舉中是否有SPRING對(duì)象,有返回該對(duì)象信息,沒(méi)有報(bào)異常
}
}
//使用enum關(guān)鍵字實(shí)現(xiàn)枚舉
enum Season1 {
//這個(gè)必須寫(xiě)到這個(gè)enum類的首行
//使用enum關(guān)鍵字,默認(rèn)會(huì)讓Season1繼承Enumeration這個(gè)類
SPRING("春季", "溫暖"),
SUMMER("夏季", "炎熱"),
AUTUMN("秋季", "肅殺"),
WINTER("冬季", "寒冷");
private String name;
private String description;
private Season1(String name, String description) {
this.name = name;
this.description = description;
}
public String getName() {
return name;
}
public String getDescription() {
return description;
}
@Override
public String toString() {
return "季節(jié):" + name + " 特點(diǎn):"+ description;
}
}
輸出:
季節(jié):秋季 特點(diǎn):肅殺
AUTUMN
2
[Lp5.Season1;@14ae5a5
季節(jié):春季 特點(diǎn):溫暖
季節(jié):夏季 特點(diǎn):炎熱
季節(jié):秋季 特點(diǎn):肅殺
季節(jié):冬季 特點(diǎn):寒冷
季節(jié):春季 特點(diǎn):溫暖
10、注解
1、注解的介紹:
1、注解也稱為元數(shù)據(jù),用于修飾解釋包、類、方法、屬性、構(gòu)造器、局部變量等信息
2、和注釋一相似之處在于它不影響代碼邏輯,不同之處注解可以被編譯貨或運(yùn)行,相當(dāng)于嵌入代碼中的補(bǔ)充信息
3、使用注解主要是為了標(biāo)記過(guò)時(shí)功能、忽略警告信息等
10.1、Override
1、用于限定某個(gè)方法,常用于重寫(xiě)父類方法,該注解只能用于方法中
10.2、Deprecated
1、用于表示某個(gè)程序中的類、方法已過(guò)時(shí)
10.3、supresswarning
1、用于屏蔽編譯器的警告信息
11、包裝類與基本數(shù)據(jù)類型
11.1、包裝類的介紹
1、在java中,有常見(jiàn)的8種基本數(shù)據(jù)類型,分別為byte、boolean、char、short、int、long、float、double,在工作中需要頻繁使用它們進(jìn)行各種操作。java設(shè)計(jì)者就為每個(gè)基本類型封裝了一個(gè)類并提供常見(jiàn)的方法,大大提高了工作效率,而這些類就是包裝類
2、基本類型 ---> 包裝類
基本數(shù)據(jù)類型 |
包裝類 |
boolean |
Boolean |
char |
Character |
byte |
Byte |
short |
Short |
int |
Integer |
long |
Long |
float |
Float |
double |
Double |
2、裝箱與拆箱
裝箱: 基本類型 ————> 包裝類型
拆箱:包裝類型 ————> 基本類型
3、裝箱拆箱舉栗:
package p5;
public class Packaging {
public static void main(String[] args) {
int i = 10;
//手動(dòng)裝箱(jdk1.5前)
Integer integer = new Integer(i);
Integer integer1 = Integer.valueOf(i);//手動(dòng)裝箱調(diào)用的其實(shí)是valueOf()方法
System.out.println(integer1.getClass());
System.out.println(integer.getClass()==integer1.getClass());
//手動(dòng)拆箱
int i1 = integer.intValue();//拆箱調(diào)用的是intValue()方法
System.out.println(i1);
//自動(dòng)裝箱
short s = 10;
Short s1 = s;//底層調(diào)用的還是Short.ValueOf(s)
System.out.println(s1.getClass());
//自動(dòng)拆箱
short s2 = s1; //底層調(diào)用的還是shortValue()進(jìn)行拆箱
System.out.println(s2);
}
}
輸出:
class java.lang.Integer
true
10
class java.lang.Short
10
11.2、小練習(xí)( 包裝類)
1、以下輸出結(jié)果是?
public static void main(String[] args) {
Integer i = new Integer(1);
Integer i1 = new Integer(1);
System.out.println(i==i1);
//false,new開(kāi)辟了新的空間,所以不是同一個(gè)對(duì)象,故false
System.out.println(i.equals(i1));
//true,底成先判斷兩對(duì)象是否為同一類型對(duì)象,在判斷兩對(duì)象的值是否相等,相等返回true,否則false
Integer m = 1;
Integer n = 1;
System.out.println(n==m);
//true,底層會(huì)先判斷該基本類型的值是否在-128~127之間,是就指向同一對(duì)象,否則new一個(gè)新對(duì)象
System.out.println(n.equals(m));
//true,底成先判斷兩對(duì)象是否為同一類型對(duì)象,在判斷兩對(duì)象的值是否相等,相等返回true,否則false
Integer x = 128;
Integer y = 128;
System.out.println(x==y);
//false,超過(guò)常量池范圍-128~127之間,new新對(duì)象,不是同一對(duì)象,故false
System.out.println(x.equals(y));
//true,底成先判斷兩對(duì)象是否為同一類型對(duì)象,在判斷兩對(duì)象的值是否相等,相等返回true,否則false
}
2、下面輸出的值是?
public static void main(String[] args){
Integer i = 127;
int i2 = 127;
System.out.println(i==i2);//true,只要比較時(shí)出現(xiàn)一個(gè)基本數(shù)據(jù)類型,比較的就是值大小
Integer i3 = 128;
int i4 = 128;
System.out.println(i3==i4);//true,只要比較時(shí)出現(xiàn)一個(gè)基本數(shù)據(jù)類型,比較的就是值大小
}
小結(jié)(包裝類)
1、當(dāng)基本數(shù)據(jù)類型與包裝類==比較時(shí),比較的是兩對(duì)象的值
2、只要是new的對(duì)象,就不可以是同一對(duì)象
3、當(dāng)兩個(gè)包裝類對(duì)象進(jìn)行==比較時(shí),如果對(duì)象值在-128~127之間,那就指向同一常量池對(duì)象
|