一個類最多只能有一個直接的父類, 但是有多個間接的父類, java是單繼承
接口概述
- 接口:拓展功能, 類似于: usb接口(可以連接外設(shè)等, 拓展功能)
- 接口的定義格式:
interface 接口名{
}
- 接口要注意的事項 :
- 接口是一個特殊的類
- 接口的成員變量默認(rèn)的修飾符為 : public static final, 也就是說接口中的成員變量都是常量
- 接口中的方法都是抽象的方法,默認(rèn)的修飾符為 : public abstract
- 接口不能創(chuàng)建對象
- 接口是沒有構(gòu)造方法的
- 接口是給類去實(shí)現(xiàn)使用的粱檀,非抽象類實(shí)現(xiàn)一個接口的時候础淤,必須要把接口中所有方法全部實(shí)現(xiàn)
實(shí)現(xiàn)接口的格式:
class 類名 implements 接口名{
}
interface A{
//成員變量
public static final int i=10;
//成員函數(shù)
public void print();
}
class Demo implements A{ // Demo就實(shí)現(xiàn)了A接口
public static void main(String[] args)
{
Demo7 d = new Demo7();
d.print();
}
//實(shí)現(xiàn)接口中的方法
public void print(){
System.out.println("這個是接口中的print方法...");
}
}
接口的作用:
- 程序的解耦 ( 低耦合 )
- 定義約束規(guī)范
- 拓展功能
//普通的鉛筆類
class Pencil{
String name;
public Pencil(String name){
this.name = name;
}
public void write(){
System.out.println(name+"沙沙的寫...");
}
}
//橡皮接口
interface Eraser{
public void remove();
}
//帶橡皮的鉛筆
class PencilWithEraser extends Pencil implements Eraser {
public PencilWithEraser(String name){
super(name);
}
public void remove(){
System.out.println("涂改,涂改....");
}
}
class Demo{
public static void main(String[] args) {
//System.out.println("Hello World!");
PencilWithEraser p = new PencilWithEraser("2B鉛筆");
p.write();
p.remove();
}
}
類與接口之間關(guān)系 : 實(shí)現(xiàn)關(guān)系
- 類與接口要注意的事項:
- 非抽象類實(shí)現(xiàn)一個接口時霹购,必須要把接口中所有方法全部實(shí)現(xiàn)
- 抽象類實(shí)現(xiàn)一個接口時贫母,可以實(shí)現(xiàn)也可以不實(shí)現(xiàn)接口中的 方法
- 一個類可以實(shí)現(xiàn)多個接口
疑問: java為什么不支持多繼承熬苍,而支持了多實(shí)現(xiàn)呢换淆?
class A{
public void print(){
System.out.println("AAAAAA");
}
}
class B{
public void print(){
System.out.println("BBBBBB");
}
}
class C extends A ,B{
}
new C().print();
接口與接口之間關(guān)系: 繼承關(guān)系
-
接口與接口之間要注意事項:
- 一個接口是可以繼承多個接口的
多態(tài)
-
面向?qū)ο蟮娜筇卣鳎?/p>
- 封裝
- 繼承
- 多態(tài)
多態(tài):一個對象具備多種形態(tài)心褐。(父類的引用類型變量指向了子類的對象)或者是接口的引用類型變量指向了接口實(shí)現(xiàn)類的對象)
多態(tài)的前提:必須存在繼承或者實(shí)現(xiàn)關(guān)系
動物 a = new 狗();-
多態(tài)要注意 的細(xì)節(jié):
- 多態(tài)情況下,子父類存在同名的成員變量時嫡锌,訪問的是父類的成員變量
- 多態(tài)情況下虑稼,子父類存在同名的非靜態(tài)的成員函數(shù)時,訪問的是子類的成員函數(shù)
- 多態(tài)情況下势木,子父類存在同名的靜態(tài)的成員函數(shù)時蛛倦,訪問的是父類的成員函數(shù)
- 多態(tài)情況下,不能訪問子類特有的成員
總結(jié):多態(tài)情況下啦桌,子父類存在同名的成員時溯壶,訪問的都是父類的成員及皂,除了在同名非靜態(tài)函數(shù)時才是訪問子類的
編譯看左邊,運(yùn)行不一定看右邊
編譯看左邊:java編譯器在編譯的時候且改,會檢查引用類型變量所屬的類是否具備指定的成員验烧,如果不具備馬上編譯報錯-
多態(tài)的應(yīng)用:
- 多態(tài)用于形參類型的時候,可以接收更多類型的數(shù)據(jù)
- 多態(tài)用于返回值類型的時候又跛,可以返回更多類型的數(shù)據(jù)
多態(tài)的好處: 提高了代碼的拓展性
類型強(qiáng)制轉(zhuǎn)換
目前多態(tài)情況下不能訪問子類特有的成員, 如果需要訪問子類特有的成員碍拆,那么需要進(jìn)行類型強(qiáng)制轉(zhuǎn)換
基本數(shù)據(jù)類型的轉(zhuǎn)換
小數(shù)據(jù)類型-------->大的數(shù)據(jù)類型 ( 自動類型轉(zhuǎn)換 )
大數(shù)據(jù)類型--------->小數(shù)據(jù)類型 ( 強(qiáng)制類型轉(zhuǎn)換 ) 小數(shù)據(jù)類型 變量名 = (小數(shù)據(jù)類型)大數(shù)據(jù)類型
引用數(shù)據(jù)類型的轉(zhuǎn)換
小數(shù)據(jù)類型--------->大數(shù)據(jù)類型 自動類型轉(zhuǎn)換
大數(shù)據(jù)類型--------->小數(shù)據(jù)類型 強(qiáng)制類型轉(zhuǎn)換類型轉(zhuǎn)換最場景的問題: java.lang.ClassCastException。 強(qiáng)制類型轉(zhuǎn)換失敗
//動物類
abstract class Animal{
String name;
public Animal(String name){
this.name = name;
}
public abstract void run();
}
//老鼠
class Mouse extends Animal{
public Mouse(String name){
super(name);
}
public void run(){
System.out.println(name+"四條腿慢慢的走!");
}
//老鼠特有方法---打洞
public void dig(){
System.out.println("老鼠在打洞..");
}
}
//魚
class Fish extends Animal{
public Fish(String name){
super(name);
}
public void run(){
System.out.println(name+"搖搖尾巴游啊游 !");
}
//吹泡泡
public void bubble(){
System.out.println(name+"吹泡泡...!");
}
}
class Demo
{
public static void main(String[] args)
{
/*
Animal a = new Mouse("老鼠"); //多態(tài)
//調(diào)用子類特有的方法
Mouse m = (Mouse)a; //強(qiáng)制類型轉(zhuǎn)換
m.dig();
*/
Mouse m = new Mouse("米老鼠");
Fish f = new Fish("草魚");
print(f);
}
//需求: 定義一個函數(shù)可以接收任意類型的動物對象慨蓝,在函數(shù)內(nèi)部要調(diào)用到動物特有的方法
public static void print(Animal a){ // Animal a = new Mouse("米老鼠");
if(a instanceof Fish){
Fish f = (Fish)a;
f.bubble();
}else if(a instanceof Mouse){
Mouse m = (Mouse)a;
m.dig();
}
}
}
多態(tài)下的類型強(qiáng)制轉(zhuǎn)換
多態(tài) : 父類的引用類型變量指向了子類的對象或者是接口類型的引用類型變量指向了接口實(shí)現(xiàn)類的對象
實(shí)現(xiàn)關(guān)系下的多態(tài):
接口 變量 = new 接口實(shí)現(xiàn)類的對象
interface Dao{ //接口的方法全部都是非靜態(tài)的方法感混。
public void add();
public void delete();
}
//接口的實(shí)現(xiàn)類
class UserDao implements Dao{
public void add(){
System.out.println("添加員工成功!礼烈!");
}
public void delete(){
System.out.println("刪除員工成功;÷!");
}
}
class Demo
{
public static void main(String[] args)
{
//實(shí)現(xiàn)關(guān)系下的多態(tài)
Dao d = new UserDao(); //接口的引用類型變量指向了接口實(shí)現(xiàn)類的對象此熬。
d.add();
}
}
內(nèi)部類
內(nèi)部類:一個類定義在另外一個類的內(nèi)部庭呜,那么該類就稱作為內(nèi)部類
內(nèi)部類的class文件名: 外部類$內(nèi)部類. 好處:便于區(qū)分該class文件是屬于哪個外部類的
-
內(nèi)部類的類別:
- 成員內(nèi)部類:
成員內(nèi)部類的訪問方式:
方式一:在外部類提供一個方法創(chuàng)建內(nèi)部類的對象進(jìn)行訪問。
方式二:在其他類直接創(chuàng)建內(nèi)部類的對象犀忱。 格式:外部類.內(nèi)部類 變量名 = new 外部類().new 內(nèi)部類();
注意: 如果是一個靜態(tài)內(nèi)部類募谎,那么在其他類創(chuàng)建的格式 : 外部類.內(nèi)部類 變量名 = new 外部類.內(nèi)部類();
內(nèi)部類的應(yīng)用場景: 我們在描述A事物的時候,發(fā)現(xiàn)描述的A事物內(nèi)部還存在另外一個比較復(fù)雜的事物B時候阴汇,而且這個比較復(fù)雜事物B還需要訪問A事物的屬性等數(shù)據(jù)近哟,那么這時候我們就可以使用內(nèi)部類描述B事物
比如: 人--->心臟
class 人{(lán)
血
氧氣
等....class 心臟{ } }
- 成員內(nèi)部類:
內(nèi)部類的好處:內(nèi)部類可以直接訪問外部類的所有成員
-
內(nèi)部類要注意的細(xì)節(jié):
- 如果外部類與內(nèi)部類存在同名的成員變量時,在內(nèi)部類中默認(rèn)情況下是訪問內(nèi)部類的成員變量, 可以通過"外部類.this.成員變量名" 指定訪問外部類的成員
- 私有的成員內(nèi)部類只能在外部類提供一個方法創(chuàng)建內(nèi)部類的對象進(jìn)行訪問鲫寄,不能在其他類創(chuàng)建對象了
- 成員內(nèi)部類一旦出現(xiàn)了靜態(tài)的成員,那么該類也必須使用static修飾
//外部類
class Outer{
//成員變量
int x = 100; // Outer.class文件被加載到內(nèi)存的時候存在內(nèi)存中疯淫。 靜態(tài)的成員數(shù)據(jù)是不需要對象存在才能訪問地来。
//成員內(nèi)部類
static class Inner{
static int i = 10;
public void print(){
System.out.println("這個是成員內(nèi)部類的print方法!"+i);
}
}
//在外部的方法中創(chuàng)建了內(nèi)部類的對象熙掺,然后調(diào)用內(nèi)部 方法未斑。
public void instance(){
Inner inner = new Inner();
inner.print();
}
}
//其他類
class Demo{
public static void main(String[] args) {
/*
System.out.println(Outer.Inner.i);
Outer outer = new Outer();
outer.instance();
Outer.Inner inner = new Outer().new Inner();
inner.print();
*/
Outer.Inner inner = new Outer.Inner();
inner.print();
}
}
局部內(nèi)部類
- 局部內(nèi)部類 : 在一個類的方法內(nèi)部定義另外一個類,那么另外一個類就稱作為局部內(nèi)部類
- 局部內(nèi)部類要注意的細(xì)節(jié):
- 如果局部 內(nèi)部類訪問了一個局部變量币绩,那么該局部變量必須使用final修飾
class Outer{
String name= "外部類的name";
public void test(){
//局部變量
final int y =100; // y 什么時候從內(nèi)存中消失蜡秽? 方法執(zhí)行完畢之后y消失。
//局部內(nèi)部類
class Inner{ /*
當(dāng)test方法執(zhí)行完畢之后缆镣,那么y馬上從內(nèi)存中消失芽突,而Inner對象在方法執(zhí)行完畢的時候還沒有從內(nèi)存中消失,而inner對象的print方法還在訪問著y變量董瞻,這時候的y變量已經(jīng)消失了寞蚌,那么就給人感覺y的生命變量已經(jīng)被延長了
解決方案: 如果一個局部內(nèi)部類訪問一個局部變量的時候田巴,那么就讓該局部內(nèi)部類訪問這個局部變量的復(fù)制品
*/
int x = 10;
public void print(){
System.out.println("這個是局部內(nèi)部類的print方法.."+y);
}
}
Inner inner = new Inner(); //這個inner對象什么時候消失? Inner對象的生命周期比局部變量y的生命周期要長挟秤。
inner.print();
}
}
class Demo{
public static void main(String[] args) {
Outer outer = new Outer();
outer.test();
}
}
內(nèi)部匿名類
- 匿名內(nèi)部類:沒有類名的類就稱作為匿名內(nèi)部類
- 匿名內(nèi)部類的好處 : 簡化書寫
- 匿名內(nèi)部類的使用前提:必須存在繼承或者實(shí)現(xiàn)關(guān)系才能使用
- 匿名內(nèi)部類一般是用于實(shí)參
abstract class Animal{
public abstract Animal run();
public abstract void sleep();
}
class Outer{
public void print(){
//需求: 在方法內(nèi)部定義一個類繼承Animal類壹哺,然后調(diào)用run方法與sleep()。
/*
//局部內(nèi)部類
class Dog extends Animal{
public void run(){
System.out.println("狗在跑..");
}
public void sleep(){
System.out.println("狗趴在睜開眼睛睡..");
}
}
//創(chuàng)建對象
Dog d = new Dog();
d.run();
d.sleep();
*/
//匿名內(nèi)部類 :匿名內(nèi)部類只是沒有類名艘刚,其他的一概成員都是具備的管宵。
// 匿名內(nèi)部類與Animal是繼承 的關(guān)系。 目前是創(chuàng)建Animal子類的對象.
Animal a = new Animal(){ //多態(tài)
//匿名內(nèi)部的成員
public Animal run(){
System.out.println("狗在跑..");
return this;
}
public void sleep(){
System.out.println("狗趴在睜開眼睛睡..");
}
//特有的方法
public void bite(){
System.out.println("狗在咬人..");
}
};
a.bite();
a.run();
a.sleep();
}
}
class Demo
{
public static void main(String[] args)
{
//System.out.println("Hello World!");
Outer outer = new Outer();
outer.print();
}
}
異常
- 異常 : 我們的java程序也是會存在某些不正常 的情況的攀甚,這些不正常的 情況我們就統(tǒng)稱異常
- 異常的體系:
----------| Throwable 所以異陈崞樱或者錯誤類的超類
--------------|Error 錯誤 錯誤一般是用于jvm或者是硬件引發(fā)的問題,所以我們一般不會通過代碼去處理錯誤的
--------------|Exception 異常 是需要通過代碼去處理的 - Throwable常用的方法:
toString() 返回當(dāng)前異常對象的完整類名+病態(tài)信息
getMessage() 返回的是創(chuàng)建Throwable傳入的字符串信息
printStackTrace() 打印異常的棧信息
class Demo
{
public static void main(String[] args)
{
/*
//創(chuàng)建了一個Throwable對象云稚。
Throwable t = new Throwable("頭暈隧饼,感冒..");
String info = t.toString();
String message = t.getMessage();
System.out.println("toString: "+ info); // java.lang.Throwable 包名+類名 = 完整類名
System.out.println("message: "+ message);
*/
test();
}
public static void test(){
//
Throwable t = new Throwable();
t.printStackTrace();
}
}
- 如何區(qū)分錯誤與異常呢:
如果程序出現(xiàn)了不正常的信息,如果不正常的信息的類名是以Error結(jié)尾的静陈,那么肯定是一個錯誤; 如果是以Exception結(jié)尾的燕雁,那么肯定就是一個異常
class Demo
{
public static void main(String[] args)
{
//java虛擬機(jī)在默認(rèn)的情況下只能管理64m內(nèi)存。
byte[] buf = new byte[1024*1024];
System.out.println("Hello World!");
}
}
異常處理
疑問 : 下面的信息是通過printStackTrace方法打印出來鲸拥,那么異常對象從何而來呢拐格?
Exception in thread "main" java.lang.ArithmeticException: / by zero
at Demo10.div(Demo10.java:10)
at Demo10.main(Demo10.java:5)jvm運(yùn)行到a/b這個語句的時候,發(fā)現(xiàn)b為0刑赶,除數(shù)為0在我們現(xiàn)實(shí)生活中是屬于不正常的情況捏浊,jvm一旦發(fā)現(xiàn)了這種不正常的情況時候,那么jvm就會馬上創(chuàng)建一個對應(yīng)的異常對象撞叨,并且會調(diào)用這個異常對象的printStackTrace的方法來處理
-
異常的處理:
- 方式一:捕獲處理
捕獲處理的格式:
try{
可能發(fā)生異常的代碼金踪;
}catch(捕獲的異常類型 變量名){
處理異常的代碼....
}
- 捕獲處理要注意的細(xì)節(jié):
1. 如果try塊中代碼出了異常經(jīng)過了處理之后,那么try-catch塊外面的代碼可以正常執(zhí)行
2. 如果try塊中出了異常的代碼牵敷,那么在try塊中出現(xiàn)異常代碼后面的代碼是不會執(zhí)行了
3. 一個try塊后面是可以跟有多個catch塊的胡岔,也就是一個try塊可以捕獲多種異常的類型
4. 一個try塊可以捕獲多種異常的類型,但是捕獲的異常類型必須從小到大進(jìn)行捕獲,否則編譯報錯
- 方式二:拋出處理(throw throws)
- 拋出處理要注意的細(xì)節(jié):
1. 如果一個方法的內(nèi)部拋出了一個異常對象, 那么必須要在方法上聲明拋出
2. 如果調(diào)用了一個聲明拋出異常 的方法, 那么調(diào)用者必須要處理異常
3. 如果一個方法內(nèi)部拋出了一個異常對象, 那么throw語句后面的代碼都不會再執(zhí)行了(一個方法遇到了throw關(guān)鍵字, 該方法也會馬上停止執(zhí)行的)
4. 在一種情況下, 只能拋出一種類型異常對象
- throw 與 throws 兩個關(guān)鍵字:
1. throw關(guān)鍵字是用于方法內(nèi)部的枷餐,throws是用于方法聲聲明上的
2. throw關(guān)鍵字是用于方法內(nèi)部拋出一個異常對象的靶瘸,throws關(guān)鍵字是用于在方法聲明上聲明拋出異常類型的
3. throw關(guān)鍵字后面只能有一個異常對象,throws后面一次可以聲明拋出多種類型的異常
- 疑問:何時使用拋出處理毛肋?何時捕獲處理怨咪?原則是如何?
如果你需要通知到調(diào)用者, 你代碼出了問題, 那么這時候就使用拋出處理
如果代碼是直接與用戶打交道遇到了異常千萬不要再拋润匙,再拋的話诗眨,就給了用戶了, 這時候就應(yīng)該使用捕獲處理
class Demo
{
public static void main(String[] args)
{
try{
int[] arr = null;
div(4,0,arr); //調(diào)用了一個 聲明拋出異常類型 的方法
}catch(Exception e){
System.out.println("出現(xiàn)異常了...");
e.printStackTrace();
}
}
public static void div(int a, int b,int[] arr) throws Exception,NullPointerException {
if(b==0){
throw new Exception(); //拋出一個異常對象...
}else if(arr==null){
throw new NullPointerException();
}
int c = a/b;
System.out.println("c="+c);
}
}
疑問一 : 異常的處理感覺沒有多大作用,因?yàn)槎际禽敵鲆粋€話而已?
異常處理非常有用孕讳,只不過是由于我們目前所接觸的知識點(diǎn)太過于局限而已疑問二: 以后捕獲處理的時候是否就是捕獲Exception即可辽话?
錯的肄鸽,因?yàn)槲覀冊诂F(xiàn)實(shí)開發(fā)中遇到不同的異常類型的時候,我往往會有不同的處理方式, 所以要分開不同的異常類型處理
class Demo
{
public static void main(String[] args)
{
int[] arr = null;
div(4,0,arr);
}
public static void div(int a , int b,int[] arr){
int c = 0;
try{
c = a/b; //jvm在這句話的時候發(fā)現(xiàn)了不正常的情況油啤,那么就會創(chuàng)建一個對應(yīng)的異常對象典徘。
System.out.println("數(shù)組的長度:"+ arr.length);
}catch(ArithmeticException e){
//處理異常的代碼....
System.out.println("異常處理了....");
System.out.println("toString:"+ e.toString());
}catch(NullPointerException e){
System.out.println("出現(xiàn)了空指針異常....");
}catch(Exception e){
System.out.println("我是急診室,包治百惨嬉А逮诲!");
}
System.out.println("c="+c);
}
}
自定義異常類
sun提供了很多的異常類給我們用于描述程序中各種的不正常情況,但是sun 給我提供異常類還不足以描述我們現(xiàn)實(shí)生活中所有不正常情況幽告,那么這時候我們就需要自定義異常類
需求: 模擬feiQ上線的時候,如果沒有插上網(wǎng)線梅鹦,那么就拋出一個沒有插上網(wǎng)線的異常,如果已經(jīng)插上了網(wǎng)上冗锁,那么就正常顯示好友列表
自定義異常類的步驟: 自定義一個類繼承Exception即可
//自定義了一個沒有網(wǎng)線的異常類
class NoIpException extends Exception{
public NoIpException(String message){
super(message); //調(diào)用了Exception一個參數(shù)的構(gòu)造函數(shù)
}
}
class Demo
{
public static void main(String[] args)
{
String ip = "192.168.10.100";
ip = null;
try{
myQ(ip); // 如果調(diào)用一個聲明拋出異常類型的方法, 那么調(diào)用者必須要處理
}catch(NoIpException e){
e.printStackTrace();
System.out.println(" 插上網(wǎng)線齐唆!");
}
}
public static void myQ(String ip) throws NoIpException{
if(ip==null){
throw new NoIpException("沒有插網(wǎng)線啊,小白冻河!");
}
System.out.println("正常顯示好友列表..");
}
}
異常體系
- Throwable 所有錯誤或者異常的父類
- Error(錯誤)
- Exception(異常) 異常一般都通過代碼處理
- 運(yùn)行時異常: 如果一個方法內(nèi)部拋出了一個運(yùn)行時異常箍邮,那么方法上 可以聲明也可以不 聲明,調(diào)用者可以以處理也可以不處理叨叙。
- 編譯時異常(非運(yùn)行時異常锭弊、受檢異常): 如果一個方法內(nèi)部拋出了一個編譯時異常對象,那么方法上就必須要聲明擂错,而且調(diào)用者也必須要處理
運(yùn)行時異常: RuntimeException以及RuntimeException子類都是屬于運(yùn)行時異常
編譯時異常: 除了運(yùn)行時異常就是編譯異常
- 疑問:為什么java編譯器會如此嚴(yán)格要求編譯時異常味滞,對運(yùn)行時異常如此寬松?
運(yùn)行時異常都是可以通過程序員良好的編程習(xí)慣去避免钮呀,所以java編譯器就沒有嚴(yán)格要求處理運(yùn)行時異常