03 Java SE 面向對象
By Kevin Song
- 03-01 類與對象
- 03-02 封裝
- 03-03 構造方法
- 03-04 單例設計模式
- 03-05 繼承
- 03-06 接口
- 03-07 多態
- 03-08 內部類
- 03-09 異常
- 03-10 Object類
- 03-11 包
面向對象的四個基本特徵
- 封裝
- 繼承
- 抽象
- 多態
03-01 類與對象
類:事物的描述
對象:該類事物的實例
描述汽車
- 屬性
- 輪胎數
- 顏色
- 行爲
- 運行
class Car {
int num;//成員變量
String color; //成員變量
void run() { //成員方法
System.out.println(num+"..."+color);
}
}
class CarDemo {
public static void main(String[ args[]) {
Car c = new Car();//c是一個類類型的引用變量,指向了該類的對象。
c.num = 4;
c.color = "Red";
c.run();
}
}
成員變量和局部變量的區別
- 定義位置不同
- 成員變量定義在類中,整個類中都可以訪問
- 局部變量定義在方法,語句,局部代碼塊中,只在所屬的區域有效
- 存放內存不同
- 成員變量存在於堆內存的對象中
- 局部變量存在於棧內存的方法中
- 釋放時間不同
- 成員變量隨着對象的創建而存在,隨着對象的消失而消失
- 局部變量隨着所屬區域的執行而存在,隨着所屬區域的結束而釋放
- 初始化不同
- 成員變量都有默認初始化值
- 局部變量沒有默認初始化值
類類型參數使用
//騎車改裝廠
public static void show(Car c) { //類類型的變量一定指向對象,要不就是null。
c.num = 3;
c.color = "black";
System.out.println(c.num+“...”+c.color);
}
匿名對象:當對象對方法僅調用一次,就可以簡化成匿名對象
new Car();//定義對象的簡寫格式
new Car().run();
03-02 封裝
隱藏對象的屬性和實現細節,僅對外提供公共訪問方式。
class Person {
private int age; //私有:只在本類中有效,外部無法訪問
public void setAge(int a) {
if (a>0 && a<130)
age = a;
else
System.out.println("數據錯誤");
}
void speak() {
System.out.println("age="+age);
}
}
clasee PersonDemo {
public static void main(String[] args) {
Person p = new Person();
p.setAge(20);
p.speak();
}
}
03-03 構造方法
構造對象的時候用的方法
特點:
- 方法名與類名相同
- 不用定義返回值類型
- 沒有具體的返回值
作用:
給單一對象進行初始化
默認構造方法:
一個類中如果沒有定義過構造方法,那麼該類中會有一個默認的空參數構造方法。
class Person {
private int age;
private String name;//私有:只在本類中有效,外部無法訪問
//定義一個Person類的構造方法
Person() {
Syetem.out.println("person run");
}
//初始化就有名字
Person(String n) {
name = n;
}
//初始化就有名字和年齡
Person(String n, int a) {
name = n;
age = a;
}
void speak() {
System.out.println("age="+age);
}
}
clasee PersonDemo {
public static void main(String[] args) {
Person p = new Person();//構造方法:構建創造對象時用的方法
Person p1= new Person("就很舒服");//重載構造方法
Person p2= new Person("就很舒服",10)//重載構造方法
}
}
構造方法和一般方法的區別:
- 構造方法:對象創建時,就會被調用,進行對象初始化
- 一般方法:對象創建後,需要方法的功能時才調用
- 構造方法:對象創建時,只調用一次
- 一般方法:對象創建後,可以被重複調用
this關鍵字
當成員變量和局部變量重名,可以用關鍵字this區分
class Person {
private int age;
private String name;//私有:只在本類中有效,外部無法訪問
//定義一個Person類的構造方法
Person(String name) {
this.name = name;//this代表當前對象
}
void speak() {
System.out.println("age="+age);
}
}
this關鍵字細節:
- 一般方法不能調用構造方法,因爲構造方法是給對象初始化,被對象所調用。
- 構造方法之間互相調用:
Person(String name) {
this.name = name;
}
Person(String name, int age) {
this(name);//必須放在構造方法第一行
this.age = age;
}
static關鍵字
- static是一個修飾符,用於修飾成員(成員變量和成員方法)
- static修飾的成員被所有對象共享
- static**優先於**對象存在,因爲static成員隨着類創建而存在
- static修飾的變量可以被類名直接調用Person.country
成員變量和靜態變量的區別:
String name;
static String country = "CN";
- 兩個變量的生命週期不同
- 成員變量隨着對象的創建而存在,隨着對象的被回收而釋放。
- 靜態變量隨着類的加載而存在,隨着類的消失而消失。
- 調用方式不同
- 成員變量只能被對象調用
- 靜態變量可以被對象和類名調用
- 別名不同
- 成員變量也稱爲實例變量
- 靜態變量稱爲類變量
- 數據存儲位置不同
- 成員變量存儲在堆內存的對象中,是對象的特有數據
- 靜態變量數據存儲在方法區,是對象的共享數據
static注意事項
- 靜態方法只能訪問靜態成員
- 靜態方法中不可以使用this或者super關鍵字
- 主方法是靜態的
主方法解析
public static void main(String[] args)
- public:因爲權限必須是最大的
- static:不需要對象的,直接用主方法所屬類名調用即可
- void:主方法沒有具體的返回值
- main:方法名,JVM識別的固定的名字
- String[] args:主方法的參數列表,是一個數組類型的參數,元素都是字符串類型
static關鍵字使用場合
- 靜態對象: 如果是相同的數據,對象不需要做修改,只需要使用即可,不需要存儲在對象中,這個變量可以用static修飾
- 靜態方法: 如果方法不需要訪問非靜態的成員變量,可以定義成static
靜態代碼塊
特點:隨着類的加載而執行。而且只執行一次
作用:給類初始化
class StaticCode {
static {
System.out.println("haha");
}
}
構造代碼塊
{//構造代碼塊。可以給所有對象進行初始化
System.out.println("Person run");
}
- 構造方法給對應的對象進行鍼對性的初始化
- 構造代碼塊給所有對象初始化
數組工具類
class ArraryToolDemo {
public static void main(String[] args) {
int[] arr = {3,5,7,72,6};
int max = ArrayTool.getIndex(arr,10);//靜態方法可以直接被類名調用,不需要創建對象,節約內存空間
System.out.println("max="+max);
}
}
/**
建立一個用於操作數組的工具類,期中包含着對數組常見的操作
@author Kevin Song
@version V1.0
*/
class ArrayTool {
private ArrayTool() {}//該類中的方法都是靜態的,所以該類不需要創建對象,爲了保證不讓其他人創建該類對象可以將構造方法私有化
/**
獲取整形數組的最大值
@param arr 接收一個元素爲int類型的數組
@return 該數組的最大的元素值
*/
public static int getMax(int[] arr) {
int max = arr[0];
for (int x =1; x<arr.length; x++) {
if (arr[x]>max)
max = arr[x];
}
return max;
}
}
03-04 單例設計模式
作用:可以保證一個類在內存中的對象是唯一的
實現思想:
- 不允許其他程序用new創建該類對象。
- 在該類創建一個本類實例
- 對外提供一個方法讓其他程序可以獲取該對象
步驟:
- 私有化該類構造方法
- 通過new在本類中創建一個本類對象
- 定義一個公有的方法,將創建的對象返回
//通過在類中創建一個對象供其他程序使用
//不允許其他程序自己new對象
//對該類對象的修改都是對同一個對象的修改
//餓漢式
class Single {//類一加載,對象就已經建立
private static Single s = new Single();
private Single(){}//私有構造方法不允許其他程序new對象
public static Single getInstance() {
return s;
}
}
//懶漢式
class Single2 {//只有調用了getInstance方法纔會建立對象
private static Single2 s = null;
private Single2() {}//私有構造方法不允許其他程序new對象
public static Single2 getInstance() {
if(s==null)
s = new Single();
return s;
}
}
class SingleDemo {
public static void main(String[] args) {
Single s = Single.getInstance();
}
}
03-05 繼承
從已有的類中派生出新的類,新的類能吸收已有類的數據屬性和行爲,並能擴展新的能力。
class Person {
String name;
int age;
}
//繼承Person類的數據
class Student extends Person {
void study() {
System.out.println(name+"..student study.."+age);
}
}
//繼承Person類的數據
class Worker extends Person {
void work() {
System.out.println(name+"..worker work.."+age);
}
}
class ExtendsDemo {
public static void main(String[] args) {
Student s = new Student();
s.name = "Kevin";
s.age = 24;
s.study;
}
}
繼承的優點:
- 提高代碼的複用性
- 讓類與類之間產生了關係,給第三個特徵多態提供了前提
Java中繼承的特點:
Java中支持單繼承。不直接支持多繼承,但對C++中的多繼承機制進行改良
- 單繼承:一個子類只能有一個直接父類
- 多繼承:一個子類可以有多個直接父類(Java中用接口進行改良)不直接支持,因爲多個父類中有相同成員,會產生調用的不確定性
java支持多層(多重)繼承
子父類中成員的特點體現:
1. 成員變量
- 本類的成員和局部變量同名用this區分本類成員變量
- 子父類中的成員變量同名用super區分父類
原理:
- this:代表一個本類對象的引用
- super:代表一個父類空間
class Fu {
private int num = 4;//子類不能直接訪問父類中的私有內容
//子類可以通過父類提供的方法間接訪問父類中的私有內容
public int getNum() {
return num;
}
}
class Zi extends Fu {
private int num = 5;
void show() {
System.out.println(this.num+"....."+super.getNum());
}
}
class ExtendsDemo2 {
public static void main(String[] args) {
Zi z = new Zi();
z.show();
}
}
2. 成員方法
方法的兩個特性:
- 重載(Overload):同一個類中,方法名相同參數列表不同的方法使用叫重載
- 重寫(Override):在子父類中,出現成員方法完全相同的情況,會運行子類方法
重寫注意事項:
- 子類方法重寫父類方法時,子類權限必須大於等於父類權限
- 靜態只能覆蓋靜態,或被靜態覆蓋
class Fu {
void show() {
System.out.println("fu show run");
}
}
class Zi extends Fu {
void show() { //重寫父類方法
System.out.println("zi show run");
super.show();
}
}
class ExtendsDemo3 {
public static void main(String[] args) {
Zi z = new Zi();
z.show();
}
}
3. 構造方法
子類構造對象時,訪問子類構造方法時,父類構造方法也會運行。
原因:在子類的構造方法中第一行有一個默認的隱式語句。super();子類的實例化過程:子類中所有的構造方法默認都會訪問父類中的空參數的構造方法
原因:子類繼承父類,獲取了父類中內容,需要了解父類是如何對內容進行初始化的。如果父類中沒有定義空參數構造方法,子類的構造方法必須在第一行用super明確要調用父類中哪個構造方法。同時子類構方法數中如果使用this調用了本類構造方法時,super就沒有了,因爲super和this都只能定義在第一行。
class Fu {
Fu() {
System.out.println("A fu run");
}
Fu(int x) {
System.out.println("B fu run");
}
}
class Zi extends Fu {
Zi() {
//super();隱式語句,調用父類中空參數的構造方法
System.out.println("C zi run");
}
Zi(int x) {
//super();隱式語句,調用父類中空參數的構造方法
System.out.println("D zi run");
}
}
class ExtendsDemo4 {
public static void main(String[] args) {
new Zi(6);//輸出結果A D
}
}
對象實例化和初始化過程
一個對象實例化過程
Person p = new Person();
- JVM讀取指定路徑下的Person.class文件,並加載進內存,並會先加載*Person的父類*
- 在堆內存中開闢空間,分配地址
- 在對象空間中,對對象中的屬性進行默認初始化
- 調用對應的構造方法進行初始化
- 在構造方法中第一行調用父類中的構造方法進行初始化
- 父類初始化完畢後對子類的屬性進行顯式初始化
- 進行子類構造方法的特定初始化
- 初始化完畢後,將地址值複製給引用變量
對象的初始化過程:
- 加載子類構造方法
- super();加載父類構造方法
- 父類構造方法調用被子類重寫的方法
- 顯示初始化
- 構造代碼塊初始化
- 子類構造方法具體初始化
class Fu {
Fu() {
System.out,println("fu constructor run");
show();
}
void show() {
System.out.println("hehe");
}
}
class Zi extends Fu {
int num = 9;
{ //第五步 構造代碼快初始化
System.out.println("constructor code..."+num);
}
Zi() { //第一步
super; //第二步
//第四步 顯示初始化
System.out.println("zi constructor..."+ num); //第六步
}
void show() { //第三步
System.out.println("show..."+num);
}
}
final關鍵字
- final是一個修飾符,可以修飾類,方法,變量
- final修飾的類不可以被繼承
- final修飾的方法不可以被重寫
- final修飾的變量是一個常量,只能被賦值一次
- 內部類只能訪問被final修飾的局部變量
class Fu { //final修飾的類不可以被繼承
void method() { //final修飾的方法不可以被重寫
//調用底層系統資源
}
}
class Zi extends Fu {
void method() {
public static final double MY_PI = 3.14; //final修飾的變量是一個常量,只能被賦值一次
System.out.println(MY_PI);
}
}
class FinalDemo {
public static void main(String[] args) {
System.out.println("Hello World");
}
}
常量命名規則:
全部大寫,不同單詞之間用_分隔。例:MY_PI
全局變量:public static修飾的變量
public static int num = 9;
全局常量:public static final修飾的變量
public static final double MY_PI = 3.14;
抽象類
當一個類描述一個事物時,沒有足夠的信息描述事物,就是抽象類
abstract class Pet { //含有抽象方法的類是抽象類
abstract void voice();//抽象修飾的方法
}
class Dog extends Pet{
void voice() {
System.out.println("woof")
}
}
class Cat extends Pet{
void voice() {
System.out.println("meow")
}
}
class AbstractDemo {
public static void main(String[] args) {
System.out.println("Hello World");
}
}
抽象類特點:
- 方法只有聲明沒有方法體 { } ,需要被abstract修飾,抽象方法必須定義在抽象類中
- 抽象類不能被實例化
- 抽象類中的抽象方法必須被子類全部重寫後纔可以被實例化,否則這個子類也是抽象類
抽象類細節:
- 抽象類中有構造方法,因爲需要給子類對象初始化
- 抽象類中可以不定義抽象方法,但是很少見,目的是不讓這個類創建對象
- abstract關鍵字不可以和這些關鍵字共存
- private:因爲子類需要重寫抽象父類,private無法訪問
- static:靜態成員可以直接被類名調用
- final:final類無法被繼承而抽象類必須被繼承,final方法無法被重寫而抽象方法必須被重寫
- 抽象類和一般類的異同
- 相同點:抽象類和一般類都用來描述事物。
- 不同點:
- 一般類有足夠的信息描述事物
抽象類描述事物的信息有可能不足 - 一般類中不能定義抽象方法
抽象類中可以定義抽象和非抽象方法 - 一般類可以被實例化
抽象類不可以被實例化
- 一般類有足夠的信息描述事物
- 抽象類一定是父類,因爲需要子類覆蓋其方法後纔可以對子類實例化。
示例:
/*
員工:
屬性:姓名,工號,薪水
行爲:工作。
程序員:
屬性:姓名,工號,薪水。
行爲:工作。
經理:
屬性:姓名,工號,薪水,獎金。
行爲:工作。
*/
abstract class Employee {
private String name;
private String id;
private double pay;
Employee(String name, String id, double pay) {
this name = name;
this id = id;
this pay = pay;
}
public abstract void work(); //沒有方法體的抽象方法
}
class Programmer extends Employee { //描述程序員
Programmer(String name, String id, double pay) {
super(name, id, pay);
}
public void work() { //加入方法體,重寫父類方法
System.out.println("programming");
}
}
class Manager extends Employee { //描述經理
Manager(String name, String id, double pay, int bonus) {
super(name, id, pay);
this.bonus = bonus;
}
public void work() { //加入方法體,重寫父類方法
System.out.println("managing");
}
}
class AbstractTest {
public static void main(String[] args) {
System.out.println("Hello World");
Programmer Kevin = new Programmer(Kevin, 1110009945, 7330);
}
}
03-06 接口
interface {}
抽象類中的方法都是抽象的抽象類就可以定義爲接口
接口中的成員都是公共權限:
- 全局常量:
public static final int NUM = 3;
- 抽象方法:
public abstract void show(); //返回值類型void可以改變
- 類與類之間是繼承關係
- 類與接口之間是實現關係
- 接口與接口之間是繼承關係,而且接口之間可以多繼承
接口不可以實例化
只能由實現了接口的子類重寫了接口中所有的方法後,該子類纔可以實例化,否則這個子類是一個抽象類
interface Demo {
public static final int NUM = 3; //全局常量
public abstract void show1(); //抽象方法
}
class DemoImp1 implements Demo {
public static final int NUM = 3;
public abstract void show1(){}
}
class InterfaceDemo {
public static void main(String[] args) {
DemoImp1 d = new DemoImp1();
System.out.println(d.NUM);
System.out.println(DemoImp1.NUM);
System.out.println(Demo.NUM);
}
接口多實現
接口的方法前修飾符是固定的都是public abstract 返回值類型,因此只要兩個接口中的方法名相同,多實現不會存在不確定性
interface A {
public void show();
}
interface B {
public void show();
}
class Test implements A, Z {
public void show(){} //接口都是抽象方法,不存在不確定性,因此可以多實現
}
一個類實現另一個類的同時,還可以實現多個接口,避免了單繼承的侷限性
class Test2 extends Q implements A,Z {}
接口和抽象類的異同點
相同點:都是向上抽取而來
不同點:
與子類的關係:
- 抽象類需要被繼承,而且只能多繼承
- 接口需要被實現,而且可以多實現
方法的定義:
- 抽象類中可以定義抽象與非抽象方法,子類繼承後可以直接使用非抽象方法
- 接口中只能定義抽象方法,必須由子類去實現
性質
- 抽象類中的繼承是is a關係,在定義該體系的基本共性內容
- 接口的實現是 like a 關係,在定義體系額外功能
示例:
//Dog Classification: GuideDog, DetectDog
abstract class Dog {
abstract void bark();
}
interface Guide {
abstract void guide();
}
class GuideDog extends Dog implement Guide{
public void bark() {}
public void guide() {}
}
接口的應用
/*
Laptop Usage
爲了擴展筆記本的功能,定義一個規則,以後不論什麼設備只要符合這個規則就可以使用
*/
interface USB {
public void open();
public void close();
}
//一年後,出現了U盤和鼠標
//實現規則
class Udisk implement USB {
public void open() {
System.out.println("Udisk Open");
}
public void open() {
System.out.println("Udisk Open");
}
}
class UsbMouse implement USB {
public void open() {
System.out.println("UsbMouse Open");
}
public void open() {
System.out.println("UsbMouse Open");
}
}
class Laptop {
public static void main(String[] args) {
useUSB(new Udisk());
useUSB(new UsbMouse());
}
//使用規則
public static void useUSB(USB u) { //接口類型的引用,用於指向接口的子類對象
u.open();
u.close();
}
}
03-07 多態
定義:某一類事物的多種存在形態
體現:父類或者接口的引用指向其子類的對象
Fu z = new Zi();
多態優缺點
- 優點:提高了代碼的擴展性,前期定義的代碼可以使用後期的內容
- 缺點:父類引用不能調用子類的特有內容
多態的前提
- 必須有關係,繼承或者實現
- 必須要重寫
/*
狗具備狗形態,也具備動物形態
貓具備貓形態,也具備動物形態
*/
abstract class Animal {
abstract void eat();
}
class Dog extends Animal {
void eat() {
System.out.println("eat bone");
}
void housekeep() {
System.out.println("house keeping");
}
}
class Cat extends Animal {
void eat() {
System.out.println("eat fish");
}
void catchMouse() {
System.out.println("catching mouse");
}
}
class DuoTaiDemo {
public static void main(String[] args) {
Animal c = new Cat();//父類引用指向子類對象
Animal d = new Dog();//父類引用指向子類對象
method(c);
method(d);
public static void method(Animal a) {
a.eat(); //Animal父類特有內容可以直接調用
if(a instanceof Cat) { //判斷對象是否爲貓類,如果是
Cat c = (Cat)a; //轉換成貓類
c.catchMouse(); //調用貓類特有方法
} else if(a instanceof Dog) { //判斷對象是否爲狗類,如果是
Dog d = (Dog)a; //轉換成狗類
d.housekeep(); //調用狗類特有方法
}
}
}
轉型
- 向上轉型:限制對特有功能的訪問
- 向下轉型:可以使用特有功能
父類引用指向子類對象時,對象只能訪問子父類共有內容。
Animal c = new Cat();//向上轉型,貓對象提升到動物類型
c.eat();//只能訪問共有功能,不能訪問特有功能
Cat c = (Cat)a;//向下轉型,貓對象降低到貓類
c.catchMouse//可以訪問特有功能
類型判斷
instanceof:用於判斷對象的具體類型。只能用於引用數據類型判斷
public static void method(Animal a) {
a.eat(); //Animal父類特有內容可以直接調用
if(a instanceof Cat) { //判斷對象是否爲貓類,如果是
Cat c = (Cat)a; //轉換成貓類
c.catchMouse(); //調用貓類特有方法
} else if(a instanceof Dog) {
Dog d = (Dog)a;
d.housekeep();
}
}
多態中的成員特點
成員變量
- 編譯時,參考引用型變量所屬的類中是否有調用的成員變量。有,編譯通過;沒有,編譯失敗。
- 運行時,參考引用型變量所屬的類中是否有調用的成員變量。並運行該所屬類中的成員變量
- 總結:參考引用型變量所屬的類中的成員變量
示例:
class Fu {
int num = 3;//如果父類沒有num,則編譯失敗
}
class Zi extends Fu {
int num = 5;
}
class DuoTaiDemo {
public static void main(String[] args) {
Fu f = new Zi();
System.out.println(f.num);// 輸出3
}
}
成員方法
- 編譯時,參考引用型變量所屬的類中是否有調用的方法。有,編譯通過;沒有,編譯失敗。
- 運行時,參考的是對象所屬的類中是否有調用的方法。並運行該所屬類中的成員變量
- 總結:編譯看左邊,運行看右邊
寫程序時只能調用父類的方法,實際運行時調用的是子類重寫父類後的方法。
class Fu {
void show() {
System.put.println("fu show");
}
}
class Zi extends Fu {
void show() {
System.put.println("zi show");
}
}
class DuoTaiDemo {
public static void main(String[] args) {
Fu f = new Zi();
f.show();//輸出"zi show"
}
}
靜態方法
- 編譯時,參考引用型變量所屬的類中是否有調用的靜態方法
- 運行時,參考引用型變量所屬的類中是否有調用的靜態方法
- 總結:編譯運行都看左邊
其實對於靜態方法,是不需要對象的,可以直接被類名調用
class Fu {
static void method() {
System.put.println("fu static");
}
}
class Zi extends Fu {
static void method() {
System.out.println("zi static");
}
}
class DuoTaiDemo {
public static void main(String[] args) {
Fu f = new Zi();
f.method();//輸出"fu static"
}
}
03-08 內部類
定義在一個類內部的類,又稱爲內置類,嵌套類
內部類訪問特點:
- 內部類可以直接訪問外部類中的成員
- 外部類需要建立內部類的對象才能訪問內部類成員
class Outer {
private int num = 3; //內部類可以直接訪問私有內容
static class Inner { //內部類
void show() {
System.out.println("show run.."+num);
}
//如果內部類中方法是靜態的,則內部類也是靜態的
static void staticShow() { //靜態直接調用,不需要對象
System.out.println("staticShow run.."+num);
}
}
//外部類需要創建內部類對象才能訪問非靜態內部類內容
public void method() {
Inner in = new Inner();
in.show();
}
}
class InnerClassDemo {
public static void main(String[] args) {
//直接訪問外部類的成員
Outer out = new Outer();
o.method;
//直接訪問外部類中的內部類中的成員
Outer.Inner in = new Outer().new Inner();
in.show();
//如果內部類是靜態的,相當於一個外部類
Outer.Inner in = new Outer.Inner();
in.show();
//如果內部類是靜態的,成員也是靜態的,直接調用,不需要對象
Outer.Inner.staticShow();
}
}
內部類可以存放在局部位置上
內部類在局部位置上只能訪問局部中被final修飾的變量
class Outer {
int num = 3;
void method() {
final int x = 9; //內部類中訪問的局部變量必須是final
class Inner {
void show() {
System.out.println("show..."+x);
}
Inner in = new Inner();
in.show();
}
}
}
匿名內部類
內部類的簡寫格式
new 父類or接口() {
子類內容;
}
前提:內部類必須繼承一個外部類,或者實現一個接口
匿名內部類其實就是一個子類對象
abstract class Demo {
abstract void show();
}
class Outer {
int num = 4;
//匿名內部類不能有名字,所以不能這樣定義
//class Inner extends Demo {
// void show() {
// System.out.println("show..."+num);
// }
//}
public void method() {
//相當於 new Inner().show();
new Demo() { //創建匿名內部類
void show() { //重寫Demo抽象方法
System.out.println("show..."+num);
}
}.show();
}
}
class InnerClassDemo {
public static void main(String[] args) {
new Outer().method();
}
}
匿名內部類應用
當方法參數是接口類型時,而且接口中的方法不超過三個,可以用匿名內部類作爲實際參數進行傳遞
interface Inter {
void show1();
void show2();
}
class Outer {
//class Inner implements Inter {
// public void show1() {}
// public void show2() {}
//}
public void method() {
// Inner in = new Inner();
// in.show1();
// in.show2();
Inter in = new Inter() { //重寫接口方法,創建匿名內部類對象
public void show1() {}
public void show2() {}
}
in.show1;
in.show2;
}
}
03-09 異常
異常:運行時發生的不正常情況,根據面向對象的思想被java封裝成了對象
異常類:在java中用類的形式對不正常的情況進行了描述和封裝對象,描述不正常的情況的類,就稱爲異常類。
class ExceptionDemo {
public static void main(Stirng[] args) {
int[] arr = new int[3];
arr = null;
System.out.println(arr[3]);
}
public static void sleep(int time) {
if(time < 0) {
//拋出一個對象
new FuTime();//新建一個負time對象,這個對象中包含問題的名稱,信息,位置等信息
}
if (time > 100000) {
//拋出一個對象
new BigTime();
}
}
}
異常體系
不正常的情況分成兩類
Throwable(可拋出):可以被throws和throw兩個關鍵字操作的類和對象都具備可拋性
- Error:不可處理的問題
特點:由JVM拋出的嚴重性的問題,這種問題一般不針對處理,直接修改代碼 - Exception:可處理的問題
該體系的特點:子類的後綴名都是用其父類名作爲後綴
ArrayIndexOutOfBoundsException
異常處理方式
自動拋出異常
//自動拋出異常:
class Demo {
public void method(int[] arr, int index) {
//JVM在這裏封裝了一個對象然後丟出:throw new ArrayIndexOutOfBoundsException(index)
System.out.println(arr[index]);
}
}
class ExceptionDemo {
public static void main(Stirng[] args) {
int[] arr = new int[3];
Demo d = new Demo();
d.method(arr,3);
}
}
手動拋出異常:
自定義異常:java中沒有定義過的異常,按照java異常的創建思想和麪向對象的思想,對異常進行自定義描述,並封裝成對象。
自定義異常類:自定義異常類必須繼承異常體系,因爲只有稱爲異常體系的子類才具有可拋性,纔可以被throw,throws操作。要麼繼承Exception,要麼繼承RuntimeException
- 繼承Exception或者其子類(編譯時異常):
- 聲明:只聲明不捕捉,無法通過編譯,可以自定義報錯的信息
- 自定義異常類,繼承Exception或者其子類
- 通過throws 類名 在方法名後聲明自定義異常類
- 通過throw new 類名(); 創建對象
- 如果需要程序通過編譯正常運行,需要用Try-Catch對拋出的異常進行處理
- 捕捉:try-catch捕捉處理異常,處理完輸出提示信息並且程序繼續運行
- 聲明:只聲明不捕捉,無法通過編譯,可以自定義報錯的信息
- 繼承RuntimeException(運行時異常):繼承了RuntimeException的子類可以不用Try-Catch或者throws就可以通過編譯。
throw:異常拋出的關鍵字
throws:聲明自定義異常類的關鍵字
throws和throw的區別
- throws使用在方法上
throw使用在方法內 - throws拋出的是異常類,可以拋出多個,用逗號隔開
throw拋出的是對象
Exception類的所有子類中分爲兩種
- 編譯時異常:其他子類
- 運行時異常:RuntimeException和其子類
異常聲明
只聲明,不處理的異常,無法通過編譯,可以自定義報錯信息
//手動拋出異常
/*
角標爲負的異常在java中沒有定義過,需要自定義
*/
class FuShuIndexException extends Exception {
FuShuIndexException() {}//空參數構造方法
FuShuIndexException(String msg) {
super(msg);//直接調用父類構造方法
}
}
class Demooo {
//創建自定義異常類對象的方法需要用throws聲明自定義異常
public int method(int[] arr, int index) throws FuShuIndexException {
if(index>=arr.length) {
//手動拋出異常
throw new ArrayIndexOutOfBoundsException("數組角標越界"+index);
} else if(index<0) {
throw new FuShuIndexException("數組角標不能是負數"+index);
}
return arr[index];
}
}
class ExceptionDemo {
//有自定義異常的主方法需要用throws聲明自定義異常
public static void main(String[] args) throws FuShuIndexException {
int[] arr = new int[3];
Demooo d = new Demooo();
d.method(arr,3);
}
}
異常捕捉
try-catch處理完程序可以繼續運行
try {
需要被檢測異常的代碼
}
catch(FuShuIndexException e) {
處理異常的代碼
}
catch(Exception e) { //多個catch,父類catch一定要放在後面
處理異常的代碼
}
finally {
一定會被執行的代碼
}
try-catch-finally代碼塊特點:
- try catch finally
- try catch:當沒有資源需要釋放時,可以不用finally
- try finally:異常無法直接catch處理,但是資源需要關閉
class FuShuIndexException extends Exception {
FuShuIndexException() {}//空參數構造方法
FuShuIndexException(String msg) {
super(msg);//直接調用父類構造方法
}
}
class Demoo {
public int method(int[] arr, int index) throws FuShuIndexException {
if(index<0) {
throw new FuShuIndexException("Index must be positive");
}
return arr[index];
}
}
class ExceptionDemo {
public static void main(String[] args) {
int[] arr = new int[3];
Demoo d = new Demoo();
try {
int num = d.method(arr, -3);
System.out.println("num=" + num);
} catch (FuShuIndexException e) {
//FuShuIndexException e = new FuShuIndexException("Index must be positive")
//獲得自定義的異常提示信息
System.out.println("Message:"+e.getMessage());
//獲得字符串形式的自定義異常類信息
System.out.println("String:"+e.toString());
//獲得出現異常的代碼位置
e.printStackTrace();
System.out.println("負數角標異常");
}
System.out.println("over");
}
}
/*
輸出:
Message:Index must be positive
FuShuIndexException: Index must be positive
String:FuShuIndexException: Index must be positive
at Demoo.method(ExceptionDemo.java:10)
負數角標異常
at ExceptionDemo.main(ExceptionDemo.java:20)
over
*/
異常處理的原則:
- 方法內如果拋出(throw)需要檢測的異常,那麼方法名後面必須要聲明(throws),否則必須在方法內部用try-catch捕捉,否則編譯失敗。
- 如果調用到了聲明異常的方法,要麼try-catch,要麼throws,否則編譯失敗
- 功能內容可以解決,就用try-catch。解決不了,用throws讓調用者解決。
- 一個方法如果拋出多個異常,那麼調用時,必須有對應多個catch進行鍼對性處理。
finally代碼塊
通常用於關閉(釋放)資源,比如關閉數據庫連接
class Demo {
public void show(int index) throws ArrayIndexOutOfBoundsException :
if(index < 0)
throw new ArrayIndexOutOfBoundsException("越界");
int[] arr = new int[3];
return arr[index];
}
}
class ExceptionDemo {
public static void main(String[] args) {
Demo d = new Demo();
try {
int num = d.show(-3);
System.out.println("num"+num);
} catch (ArrayIndexOutOfBoundsException e) {
System.out.println(e.toString());
//return; 如果加了return;則不會運行over
//System.exit(o);如果加了這句,則不會運行finally代碼塊,直接退出JVM
} finally { //一定會被執行,除非System.exit(0);退出JVM
System.out.println("finally");
}
System.out.println("over");
}
}
異常應用
//自定義藍屏異常類
class BlueScreenException extends Exception {
BlueScreenException(String msg) {
Super(msg);
}
}
//自定義冒煙異常類
class MaoYanException extends Exception {
MaoYanException(String msg) {
Super(msg);
}
}
//自定義無法處理異常類
class NoPlanException extends Exception {
NoPlanException(String msg) {
Super(msg);
}
}
class Computer {
private int state = 0;
//定義電腦運行功能
public void run() throws BlueScreenException, MaoYanException {
if(state == 1) { //狀態值爲1時電腦藍屏
throw new BlueScreenException("藍屏");
} else if(state == 2)
throw new MaoYanException("冒煙");
System.out.println("電腦運行");
}
//定義電腦重啓功能
public void reboot() {
state = 0; //重置電腦到正常狀態
System.out.println("重啓");
}
}
class Teacher {
private String name;
private Computer comp;
Teacher(String name) {
this.name = name;
}
public void teach() throws NoPlanException {
try {
comp.run(); //電腦運行
System.out.println("講課"); //正常講課
} catch (BlueScreenException e) {
System.out.println(e.toString());
comp.reset();
teach();
} catch (MaoYanException e) {
System.out.println(e.toString());
test();
//異常轉換:收到冒煙異常,轉換成NoPlan異常對外進行告知
throw new NoPlanException("課時進度無法完成");
}
}
public void test() {
System.out.println("大家練習");
}
}
class ExceptionTest {
public static void main(String[] args) {
Teacher t = new Teacher("Kevin");
try (
t.teach();
) catch(NoPlanException e) {
System.out.println(e.toString()+"...");
System.out.println("換老師");
}
}
}
異常的注意事項:
- 子類在重寫父類方法時,父類的方法如果跑出了異常,那麼子類的方法只能拋出父類的異常或者該異常的子類
- 如果父類拋出多個異常,那麼子類只能拋出父類異常的子集
03-10 Object類
Object類是所有類的根類,是不斷抽取而來,具備所有對象都具備的共性內容
Object類常用方法
equals方法
當兩個引用值指向同一個對象時才返回true
class Person extends Object {
private int age;
Person(int age) {
this.age = age;
}
}
Class EqualsDemo {
public static void main(String[] args) {
Person p1 = new Person();
Person p2 = new Person();
Person p3 = p1;
System.out.println(p1 == p2);//false
System.out.println(p1.equals(p2));//false
System.out.println(p1.equals(p3));//true
}
}
equals方法重寫
equals方法一般都會被重寫,根據對象的特有內容,建立判斷對象是否相同的依據
class Person extends Object {
private int age;
Person(int age) {
this.age = age;
}
public boolean equals(Object obj) {//重寫Object類中的equals方法,進行對象年齡比較
if(!(obj instanceof Person)) {
return false;
throw new ClassCastException("類型錯誤");
}
Person p = (Person)obj;
return this.age == p.age;
}
}
Class EqualsDemo {
public static void main(String[] args) {
Person p1 = new Person(20);
Person p2 = new Person(20);
System.out.println(p1 == p2);//false
System.out.println(p1.equals(p2));//true
System.out.println(p1.equals(new Demo()));//報錯,類型錯誤
}
}
hashCode方法
判斷兩個對象是否完全相同
class Person extends Object {
private int age;
Person(int age) {
this.age = age;
}
public int hashCode() { //重寫object類中的hashCode方法,輸出年齡
return age;
}
}
Class EqualsDemo {
public static void main(String[] args) {
Person p1 = new Person(20);
Person p2 = new Person(20);
System.out.println(p1);//輸出p1的哈希值
System.out.println(p1.hashCode());//輸出p1的哈希值
System.out.println(Integer.toHexString(p1.hashCode()));//輸出p1的十六進制哈希值
}
}
getClass方法
獲取當前對象所屬字節碼對象
Class clazz1 = p1.getClass();
Class clazz1 = p2.getClass();
System.out.println(clazz1 == clazz2);//true
System.out.println(clazz1.getNma());//Person
toString方法
返回對象的字符串表示形式,它的值等於:
getClass().getNmae() + '@' + Integer.toHexString(hashCode())
建議重寫該方法
public String toString() {
return "Person: " + age;
}
System.out.println(p1);//Person: 20
System.out.println(p1.getClass().getNmae() + '@' + Integer.toHexString(hashCode()));////Person@61de33
03-11 包
對類文件進行分類管理
package allpack.boypack.mypack;
class PackageDemo {
public static void main(String[] args) {
System.out.println("Hello Package!");
}
}
//javac -d . PackageDemo.java
//java mypack.PackageDemo
包與包之間的訪問
包與包之間的類進行訪問,被訪問的包中的類必須是public的,被訪問的包中的類的方法也必須是public
package packa;
public class DemoA { //包與包之間訪問必須是public
public void show() //包與包之間訪問必須是public{
System.out.println("demoa show run");
}
}
package packb;
public class DemoA { //包與包之間訪問必須是public
protected void method() { //protected只能被繼承了父類的訪問
System.out.println("demoa show run");
}
}
package mypack;
class PackageDemo {
public static void main(String[] args) {
packa.DemoA d = new packa DemoA();
d.show();
System.out.println("Hello Package!");
}
}
位置 | public | protected | default | private |
---|---|---|---|---|
同一類中 | ok | ok | ok | ok |
同一包中 | ok | ok | ok | |
子類中 | ok | ok | ||
不同包中 | ok |
導入import
package packa;
public class DemoA { //包與包之間訪問必須是public
public void show() //包與包之間訪問必須是public{
System.out.println("demoa show run");
}
}
package packb;
public class DemoA { //包與包之間訪問必須是public
protected void method() {
System.out.println("demoa show run");
}
}
package mypack;
import packa.*//導入了packa包中所有的類
class PackageDemo {
public static void main(String[] args) {
packa.DemoA d = new packa DemoA();
d.show();
System.out.println("Hello Package!");
}
}
Jar包
Java的壓縮包