1.注釋
注釋的重要性不言而喻,我們不管寫什么代碼注釋必不可少,那么java的注釋的書寫方式與注釋模板是怎么進行的呢?我們來看一下。
package frist;
/*
* @Description HelloWorld類
* @Author 王延領
**/
class HelloWorld {
/*
這是我們Java程序的主入口,
main方法也是程序的主線程。
*/
public static void main(String[] arg)
{
//輸出
System.out.println("wyl");
}
}
1.1 注釋
以上可以看出java的注釋主要有三種
單行注釋:只能注釋當前行,以//開始,直到行結束
//輸出
多行注釋:注釋一段文字,以/*開始, */結束!
/*
這是我們Java程序的主入口,
main方法也是程序的主線程。
*/
文檔注釋:用於生產API文檔,配合JavaDoc。
/*
* @Description HelloWorld類
* @Author 王延領
**/
1.2 idea注釋模版配置
1.2.1 定義java文件頭部的注釋
File => setting => editor => File and Code Templates-class -Includes

/**
* @創建人 王延領
*@創建時間 ${DATE}
*描述 Todo
**/
以上當你創建一個class的時候就會帶上以上信息了
1.2.2 給java類中的方法添加上注釋
第一步勾選Enable Live Templates
首先要在上一步中勾選中 Enable Live Templates

第二步新建一個Group
其次要打開LiveTemplates 然后新建一個Group
如圖:

在彈窗口中輸入你想要的group名稱,wyl

其中:Abbreviation 為快捷鍵,當輸入w的時候就會提示對應的方法注釋模板,j為類的注釋模板

Templete Text
注釋內容,$$ 為動態模板參數點擊Edit Vaariables 選擇對應動態值。

/*
* @描述: TODO
* @作者 王延領
* @時間 2021/7/12
* @版本 1.0
*/
public class wyl {
/**
*@描述
*@參數 [str]
*@返回值 [java.lang.String]
*@創建人 王延領
*@創建時間 2021/7/12
*@修改人和其它信息
*/
public String CommentTemplate(String str)
{
return str;
}
}
2.關鍵字
| 關鍵字 | 說明 |
|---|---|
| private | 一種訪問控制方式:私用模式 |
| protected | 一種訪問控制方式:保護模式 |
| public | 一種訪問控制方式:共用模式 |
| abstract | 表明類或者成員方法具有抽象屬性 |
| class | 類 |
| extends | 表明一個類型是另一個類型的子類型,這里常見的類型有類和接口 |
| final | 用來說明最終屬性,表明一個類不能派生出子類,或者成員方法不能被覆蓋,或者成員域的值不能被改變 |
| implements | 表明一個類實現了給定的接口 |
| interface | 接口 |
| native | 用來聲明一個方法是由與計算機相關的語言(如C/C++/FORTRAN語言)實現的 |
| new | 用來創建新實例對象 |
| static | 表明具有靜態屬性 |
| strictfp | 用來聲明FP_strict(單精度或雙精度浮點數)表達式遵循IEEE 754算術規范 |
| synchronized | 表明一段代碼需要同步執行 |
| transient | 聲明不用序列化的成員域 |
| volatile | 表明兩個或者多個變量必須同步地發生變化 |
| break | 提前跳出一個塊 |
| continue | 回到一個塊的開始處 |
| return | 從成員方法中返回數據 |
| do | 用在do-while循環結構中 |
| while | 用在循環結構中 |
| if | 條件語句的引導詞 |
| else | 用在條件語句中,表明當條件不成立時的分支 |
| for | 一種循環結構的引導詞 |
| instanceof | 用來測試一個對象是否是指定類型的實例對象 |
| switch | 分支語句結構的引導詞 |
| case | 用在switch語句之中,表示其中的一個分支 |
| default | 默認,例如,用在switch語句中,表明一個默認的分支 |
| try | 嘗試一個可能拋出異常的程序塊 |
| catch | 用在異常處理中,用來捕捉異常 |
| throw | 拋出一個異常 |
| throws | 聲明在當前定義的成員方法中所有需要拋出的異常 |
| import | 表明要訪問指定的類或包 |
| package | 包 |
| boolean | 基本數據類型之一,布爾類型 |
| byte | 基本數據類型之一,字節類型 |
| char | 基本數據類型之一,字符類型 |
| double | 基本數據類型之一,雙精度浮點數類型 |
| float | 基本數據類型之一,單精度浮點數類型 |
| int | 基本數據類型之一,整數類型 |
| long | 基本數據類型之一,長整數類型 |
| short | 基本數據類型之一,短整數類型 |
| super | 表明當前對象的父類型的引用或者父類型的構造方法 |
| this | 指向當前實例對象的引用 |
| void | 聲明當前成員方法沒有返回值 |
| goto | 保留關鍵字,沒有具體含義 |
| const | 保留關鍵字,沒有具體含義 |
3.數據類型

3.1.數據類型轉換
3.1.1自動類型轉換
自動類型轉換:容量小的數據類型可以自動轉換為容量大的數據類型。

注:如果低級類型為char型,向高級類型(整型)轉換時,會轉換為對應ASCII碼值
3.1.2 強制類型轉換
強制類型轉換,又被稱為造型,用於顯式的轉換一個數值的類型.
轉換方式為:(type)var ,運算符“()”中的type表示將值var想要轉換成的目標數據類型。 條件是轉換的數據類型必須是兼容的。
double x = 3.14;
int nx = (int)x; //值為3
char c = 'a';
int d = c+1;
System.out.println(d); //98
System.out.println((char)d); //b
3.1.3.包裝類過渡類型轉換
eg1:int i=Integer.parseInt(“123”)
說明:此方法只能適用於字符串轉化成整型變量
eg2: float f=Float.valueOf(“123”).floatValue()
說明:上例是將一個字符串轉化成一個Float對象,然后再調用這個對象的floatValue()方法返回其對應的float數值。
eg3: boolean b=Boolean.valueOf(“123”).booleanValue()
說明:上例是將一個字符串轉化成一個Boolean對象,然后再調用這個對象的booleanValue()方法返回其對應的boolean數值。
eg4:double d=Double.valueOf(“123”).doublue()
說明:上例是將一個字符串轉化成一個Double對象,然后再調用這個對象的doublue()方法返回其對應的double數值。
eg5: long l=Long.valueOf(“123”).longValue()
說明:上例是將一個字符串轉化成一個Long對象,然后再調用這個對象的longValue()方法返回其對應的long數值。
eg6: char=Character.valueOf(“123”).charValue()
說明:上例是將一個字符串轉化成一個Character對象
4.常量、變量、運算符
常量
變量是什么:就是可以變化的量!
我們通過變量來操縱存儲空間中的數據,變量就是指代這個存儲空間!空間位置是確定的,但是里面放
置什么值不確定!Java是一種強類型語言,每個變量都必須聲明其類型。
//數據類型 變量名 = 值;可以使用逗號隔開來聲明多個同類型變量。
注意事項:
每個變量都有類型,類型可以是基本類型,也可以是引用類型。
變量名必須是合法的標識符。
變量聲明是一條完整的語句,因此每一個聲明都必須以分號結束
變量作用域
類變量(靜態變量: static variable):獨立於方法之外的變量,用 static 修飾。
實例變量(成員變量:member variable):獨立於方法之外的變量,不過沒有 static 修飾。
局部變量(lacal variable):類的方法中的變量。
變量
常量(Constant):初始化(initialize)后不能再改變值!不會變動的值。
final 常量名=值;
final double PI=3.14;
命名規范
- 所有變量、方法、類名:見名知意
- 類成員變量:首字母小寫和駝峰原則 : monthSalary
- 局部變量:首字母小寫和駝峰原則
- 常量:大寫字母和下划線:MAX_VALUE
- 類名:首字母大寫和駝峰原則: Man, GoodMan
- 方法名:首字母小寫和駝峰原則: run(), runRun()
運算符
Java 語言支持如下運算符:
算術運算符: +,-,,/,%,++,--
賦值運算符 =
關系運算符: >,<,>=,<=,==,!= instanceof
邏輯運算符: &&,||,!
位運算符: &,|,^,~ , >>,<<,>>> (了解!!!)
條件運算符 ?:
擴展賦值運算符:+=,-=,=,/=
5.java流轉控制
if...else、while、do...while、for、switch...case 在這就不累述了。
跳轉:
return
return從一個方法返回,並把控制權交給調用它的語句序;或者直接結束當前的程序;
break
break語句在for、while、do···while循環語句中,經常用於強行退出當前循環;
continue
continue語句用於跳過此次循環,執行下次循環;
6.方法
那么什么是方法呢?
Java方法是語句的集合,它們在一起執行一個功能。
方法是解決一類問題的步驟的有序組合
方法包含於類或對象中
方法在程序中被創建,在其他地方被引用
設計方法的原則:方法的本意是功能塊,就是實現某個功能的語句塊的集合。我們設計方法的時候,最
好保持方法的原子性,就是一個方法只完成1個功能,這樣利於我們后期的擴展。
方法的優點
使程序變得更簡短而清晰。
有利於程序維護。
可以提高程序開發的效率。
提高了代碼的重用性。
定義
修飾符 返回值類型 方法名(參數類型 參數名){
...
方法體
...
return 返回值;
}
修飾符:修飾符,這是可選的,告訴編譯器如何調用該方法。定義了該方法的訪問類型。
返回值類型 :方法可能會返回值。returnValueType 是方法返回值的數據類型。有些方法執行所需
的操作,但沒有返回值。在這種情況下,returnValueType 是關鍵字void。
方法名:是方法的實際名稱。方法名和參數表共同構成方法簽名。
參數類型:參數像是一個占位符。當方法被調用時,傳遞值給參數。這個值被稱為實參或變量。參
數列表是指方法的參數類型、順序和參數的個數。參數是可選的,方法可以不包含任何參數。
形式參數:在方法被調用時用於接收外界輸入的數據。
實參:調用方法時實際傳給方法的數據。
方法體:方法體包含具體的語句,定義該方法的功能。
方法的重載
就是說一個類的兩個方法擁有相同的名字,但是有不同的參數列表。
可變參數
在方法聲明中,在指定參數類型后加一個省略號(...) 。
一個方法中只能指定一個可變參數,它必須是方法的最后一個參數。任何普通的參數必須在它之前聲
明。
typeName... parameterName
遞歸
自己調用自己
7.數組
數組的定義:
數組是相同類型數據的有序集合.
數組描述的是相同類型的若干個數據,按照一定的先后次序排列組合而成。
其中,每一個數據稱作一個數組元素,每個數組元素可以通過一個下標來訪問它們.
數組的四個基本特點:
- 其長度是確定的。數組一旦被創建,它的大小就是不可以改變的。
- 其元素必須是相同類型,不允許出現混合類型。
- 數組中的元素可以是任何數據類型,包括基本類型和引用類型。
- 數組變量屬引用類型,數組也可以看成是對象,數組中的每個元素相當於該對象的成員變量
數組聲明
dataType[] arrayRefVar; // 首選的方法
或
dataType arrayRefVar[]; // 效果相同,但不是首選方法
創建數組
arrayRefVar = new dataType[1 arraySize];
數組的元素是通過索引訪問的。數組索引從 0 開始,所以索引值從 0 到 arrayRefVar.length-1。
三種初始化
靜態初始化
除了用new關鍵字來產生數組以外,還可以直接在定義數組的同時就為數組元素分配空間並賦值。
int[] a = {1,2,3};
Man[] mans = {new Man(1,1),new Man(2,2)};
動態初始化
數組定義、為數組元素分配空間、賦值的操作、分開進行。
int[] a = new int[2];
a[0]=1;
a[1]=2;
數組的默認初始化
數組是引用類型,它的元素相當於類的實例變量,因此數組一經分配空間,其中的每個元素也被按照實
例變量同樣的方式被隱式初始化。
public static void main(String[] args) {
int[] a=new int[2];
boolean[] b = new boolean[2];
String[] s = new String[2];
System.out.println(a[0]+":"+a[1]); //0,0
System.out.println(b[0]+":"+b[1]); //false,false
System.out.println(s[0]+":"+s[1]); //null, null
}
數組邊界
下標的合法區間:[0, length-1],如果越界就會報錯;
for 和For-Each 循環
for(type element: array){
System.out.println(element);
}
for (int i = 1; i < myList.length; i++) {
System.out.println(myList[i]);
}
多維數組
type[][] typeName = new type[typeLength1][1 typeLength2];
Arrays 類
數組的工具類java.util.Arrays
java.util.Arrays 類能方便地操作數組. 使用之前需要導包!
具有以下常用功能:
給數組賦值:通過 fill 方法。
對數組排序:通過 sort 方法,按升序。
比較數組:通過 equals 方法比較數組中元素值是否相等。
查找數組元素:通過 binarySearch 方法能對排序好的數組進行二分查找法操作。
轉換為list: 通過asList(a)進行轉換
8.面向對象
萬物皆為對象!!!對象是抽象概念的具體實例。
以類的方式組織代碼,以對象的組織(封裝)數據就是面向對象
繼承
繼承是java面向對象編程技術的一塊基石,因為它允許創建分等級層次的類。
class 父類 {
}
class 子類 extends 父類 {
}
public interface A {
public void eat();
public void sleep();
}
public interface B {
public void show();
}
public class C implements A,B {
}
為什么要繼承,因為有重復。所以才繼承,進而我們就知道了。父類就是公共部分的定義或規則
Java 不支持多繼承(只能繼承一個類),但支持多重繼承。

特點
-
子類擁有父類非 private 的屬性、方法。
-
子類可以擁有自己的屬性和方法,即子類可以對父類進行擴展。
-
子類可以用自己的方式實現父類的方法。
-
Java 的繼承是單繼承,但是可以多重繼承,單繼承就是一個子類只能繼承一個父類,多重繼承就是,例如 B 類繼承 A 類,C 類繼承 B 類,所以按照關系就是 B 類是 C 類的父類,A 類是 B 類的父類,這是 Java 繼承區別於 C++ 繼承的一個特性。
-
提高了類之間的耦合性(繼承的缺點,耦合度高就會造成代碼之間的聯系越緊密,代碼獨立性越差)
super 與 this 關鍵字
class Animal {
void eat() {
System.out.println("animal : eat");
}
}
class Dog extends Animal {
void eat() {
System.out.println("dog : eat");
}
void eatTest() {
this.eat(); // this 調用自己的方法
super.eat(); // super 調用父類方法
}
}
public class Test {
public static void main(String[] args) {
Animal a = new Animal();
a.eat();
Dog d = new Dog();
d.eatTest();
}
}
animal : eat
dog : eat
animal : eat
final關鍵字
final 關鍵字聲明類可以把類定義為不能繼承的,即最終類;
class SuperClass {
private int n;
SuperClass(){
System.out.println("SuperClass()");
}
SuperClass(int n) {
System.out.println("SuperClass(int n)");
this.n = n;
}
}
// SubClass 類繼承
class SubClass extends SuperClass{
private int n;
SubClass(){ // 自動調用父類的無參數構造器
System.out.println("SubClass");
}
public SubClass(int n){
super(300); // 調用父類中帶有參數的構造器
System.out.println("SubClass(int n):"+n);
this.n = n;
}
}
// SubClass2 類繼承
class SubClass2 extends SuperClass{
private int n;
SubClass2(){
super(300); // 調用父類中帶有參數的構造器
System.out.println("SubClass2");
}
public SubClass2(int n){ // 自動調用父類的無參數構造器
System.out.println("SubClass2(int n):"+n);
this.n = n;
}
}
public class TestSuperSub{
public static void main (String args[]){
System.out.println("------SubClass 類繼承------");
SubClass sc1 = new SubClass();
SubClass sc2 = new SubClass(100);
System.out.println("------SubClass2 類繼承------");
SubClass2 sc3 = new SubClass2();
SubClass2 sc4 = new SubClass2(200);
}
}
------SubClass 類繼承------
SuperClass()
SubClass
SuperClass(int n)
SubClass(int n):100
------SubClass2 類繼承------
SuperClass(int n)
SubClass2
SuperClass()
SubClass2(int n):200
構造函數
子類是不繼承父類的構造器(構造方法或者構造函數)的,它只是調用(隱式或顯式)。如果父類的構造器帶有參數,則必須在子類的構造器中顯式地通過 super 關鍵字調用父類的構造器並配以適當的參數列表。
如果父類構造器沒有參數,則在子類的構造器中不需要使用 super 關鍵字調用父類構造器,系統會自動調用父類的無參構造器。
重寫(Override)與重載(Overload)
重寫(Override)
重寫是子類對父類的允許訪問的方法的實現過程進行重新編寫, 返回值和形參都不能改變。即外殼不變,核心重寫
class Animal{
public void move(){
System.out.println("動物可以移動");
}
}
class Dog extends Animal{
public void move(){
System.out.println("狗可以跑和走");
}
}
public class TestDog{
public static void main(String args[]){
Animal a = new Animal(); // Animal 對象
Animal b = new Dog(); // Dog 對象
a.move();// 執行 Animal 類的方法
b.move();//執行 Dog 類的方法
}
}
動物可以移動
狗可以跑和走
方法的重寫規則
-
參數列表與被重寫方法的參數列表必須完全相同。
-
返回類型與被重寫方法的返回類型可以不相同,但是必須是父類返回值的派生類(java5 及更早版本返回類型要一樣,java7 及更高版本可以不同)。
-
訪問權限不能比父類中被重寫的方法的訪問權限更低。例如:如果父類的一個方法被聲明為 public,那么在子類中重寫該方法就不能聲明為 protected。
-
父類的成員方法只能被它的子類重寫。
-
聲明為 final 的方法不能被重寫。
-
聲明為 static 的方法不能被重寫,但是能夠被再次聲明。
-
子類和父類在同一個包中,那么子類可以重寫父類所有方法,除了聲明為 private 和 final 的方法。
-
子類和父類不在同一個包中,那么子類只能夠重寫父類的聲明為 public 和 protected 的非 final 方法。
-
重寫的方法能夠拋出任何非強制異常,無論被重寫的方法是否拋出異常。但是,重寫的方法不能拋出新的強制性異常,或者比被重寫方法聲明的更廣泛的強制性異常,反之則可以。
-
構造方法不能被重寫。
-
如果不能繼承一個類,則不能重寫該類的方法。
重載(Overload)
重載(overloading) 是在一個類里面,方法名字相同,而參數不同。返回類型可以相同也可以不同。
每個重載的方法(或者構造函數)都必須有一個獨一無二的參數類型列表。
最常用的地方就是構造器的重載。
public class Overloading {
public int test(){
System.out.println("test1");
return 1;
}
public void test(int a){
System.out.println("test2");
}
//以下兩個參數類型順序不同
public String test(int a,String s){
System.out.println("test3");
return "returntest3";
}
public String test(String s,int a){
System.out.println("test4");
return "returntest4";
}
public static void main(String[] args){
Overloading o = new Overloading();
System.out.println(o.test());
o.test(1);
System.out.println(o.test(1,"test3"));
System.out.println(o.test("test4",1));
}
}
重載規則:
-
被重載的方法必須改變參數列表(參數個數或類型不一樣);
-
被重載的方法可以改變返回類型;
-
被重載的方法可以改變訪問修飾符;
-
被重載的方法可以聲明新的或更廣的檢查異常;
-
方法能夠在同一個類中或者在一個子類中被重載。
-
無法以返回值類型作為重載函數的區分標准。
區別點 重載方法 重寫方法 參數列表 必須修改 一定不能修改 返回類型 可以修改 一定不能修改 異常 可以修改 可以減少或刪除,一定不能拋出新的或者更廣的異常 訪問 可以修改 一定不能做更嚴格的限制(可以降低限制)
多態
多態是同一個行為具有多個不同表現形式或形態的能力。
多態就是同一個接口,使用不同的實例而執行不同操作。
多態性是對象多種表現形式的體現。
多態的優點
- 消除類型之間的耦合關系
- 可替換性
- 可擴充性
- 接口性
- 靈活性
- 簡化性
多態存在的三個必要條件
繼承
重寫
父類引用指向子類對象:Parent p = new Child();
class Shape {
void draw() {}
}
class Circle extends Shape {
void draw() {
System.out.println("Circle.draw()");
}
}
class Square extends Shape {
void draw() {
System.out.println("Square.draw()");
}
}
class Triangle extends Shape {
void draw() {
System.out.println("Triangle.draw()");
}
}
虛函數
虛函數的存在是為了多態。
Java 中其實沒有虛函數的概念,它的普通函數就相當於 C++ 的虛函數,動態綁定是Java的默認行為。如果 Java 中不希望某個函數具有虛函數特性,可以加上 final 關鍵字變成非虛函數。
多態的實現方式
方式一:重寫:
方式二:接口
方式三:抽象類和抽象方法
抽象類
擁有抽象方法的類就是抽象類,抽象類要使用abstract關鍵字聲明.
abstract class A{//定義一個抽象類
public void fun(){//普通方法
System.out.println("存在方法體的方法");
}
public abstract void print();//抽象方法,沒有方法體,有abstract關鍵字做修飾
}
繼承抽象類
我們可以通過以下方式繼承 Employee 類的屬性
抽象類的使用原則
(1)抽象方法必須為public或者protected(因為如果為private,則不能被子類繼承,子類便無法實現該方法),缺省情況下默認為public;
(2)抽象類不能直接實例化,需要依靠子類采用向上轉型的方式處理;
(3)抽象類必須有子類,使用extends繼承,一個子類只能繼承一個抽象類;
(4)子類(如果不是抽象類)則必須覆寫抽象類之中的全部抽象方法(如果子類沒有實現父類的抽象方法,則必須將子類也定義為為abstract類。);
package com.wz.abstractdemo;
abstract class A{//定義一個抽象類
public void fun(){//普通方法
System.out.println("存在方法體的方法");
}
public abstract void print();//抽象方法,沒有方法體,有abstract關鍵字做修飾
}
//單繼承
class B extends A{//B類是抽象類的子類,是一個普通類
@Override
public void print() {//強制要求覆寫
System.out.println("Hello World !");
}
}
public class TestDemo {
public static void main(String[] args) {
A a = new B();//向上轉型
a.print();//被子類所覆寫的過的方法
}
}
Hello World !
封裝
封裝(英語:Encapsulation)是指一種將抽象性函式接口的實現細節部分包裝、隱藏起來的方法。
封裝可以被認為是一個保護屏障,防止該類的代碼和數據被外部類定義的代碼隨機訪問。
要訪問該類的代碼和數據,必須通過嚴格的接口控制。
封裝最主要的功能在於我們能修改自己的實現代碼,而不用修改那些調用我們代碼的程序片段。
適當的封裝可以讓程式碼更容易理解與維護,也加強了程式碼的安全性。
封裝的優點
- 良好的封裝能夠減少耦合。
- 類內部的結構可以自由修改。
- 可以對成員變量進行更精確的控制。
- 隱藏信息,實現細節。
接口
在JAVA編程語言中是一個抽象類型,是抽象方法的集合,接口通常以interface來聲明。一個類通過繼承接口的方式,從而來繼承接口的抽象方法。
接口與類相似點
- 一個接口可以有多個方法。
- 接口文件保存在 .java 結尾的文件中,文件名使用接口名。
- 接口的字節碼文件保存在 .class 結尾的文件中。
- 接口相應的字節碼文件必須在與包名稱相匹配的目錄結構中。
接口與類的區別
- 接口不能用於實例化對象。
- 接口沒有構造方法。
- 接口中所有的方法必須是抽象方法,Java 8 之后 接口中可以使用 default 關鍵字修飾的非抽象方法。
- 接口不能包含成員變量,除了 static 和 final 變量。
- 接口不是被類繼承了,而是要被類實現。
- 接口支持多繼承。
接口特性
- 接口中每一個方法也是隱式抽象的,接口中的方法會被隱式的指定為 public abstract(只能是 public abstract,其他修飾符都會報錯)。
- 接口中可以含有變量,但是接口中的變量會被隱式的指定為 public static final 變量(並且只能是 public,用 private 修飾會報編譯錯誤)。
- 接口中的方法是不能在接口中實現的,只能由實現接口的類來實現接口中的方法。
抽象類和接口的區別
-
抽象類中的方法可以有方法體,就是能實現方法的具體功能,但是接口中的方法不行。
-
抽象類中的成員變量可以是各種類型的,而接口中的成員變量只能是 public static final 類型的。
-
接口中不能含有靜態代碼塊以及靜態方法(用 static 修飾的方法),而抽象類是可以有靜態代碼塊和靜態方法
-
一個類只能繼承一個抽象類,而一個類卻可以實現多個接口。
[可見度] interface 接口名稱 [extends 其他的接口名] {
// 聲明變量
// 抽象方法
}
/* 文件名 : NameOfInterface.java */
import java.lang.*;
//引入包
public interface NameOfInterface
{
//任何類型 final, static 字段
//抽象方法
}
接口有以下特性
- 接口是隱式抽象的,當聲明一個接口的時候,不必使用abstract關鍵字。
- 接口中每一個方法也是隱式抽象的,聲明時同樣不需要abstract關鍵字。
- 接口中的方法都是公有的。
枚舉
枚舉是一個特殊的類,一般表示一組常量.每個枚舉都是通過 Class 在內部實現的,且所有的枚舉值都是 public static final 的。
enum Color
{
RED, GREEN, BLUE;
}
public class Test
{
// 執行輸出結果
public static void main(String[] args)
{
Color c1 = Color.RED;
System.out.println(c1);
}
}
RED
values(), ordinal() 和 valueOf() 方法
enum 定義的枚舉類默認繼承了 java.lang.Enum 類,並實現了 java.lang.Seriablizable 和 java.lang.Comparable 兩個接口。
values(), ordinal() 和 valueOf() 方法位於 java.lang.Enum 類中:
-
values() 返回枚舉類中所有的值。
-
ordinal()方法可以找到每個枚舉常量的索引,就像數組索引一樣。
-
valueOf()方法返回指定字符串值的枚舉常量。
enum Color { RED, GREEN, BLUE; } public class Test { public static void main(String[] args) { // 調用 values() Color[] arr = Color.values(); // 迭代枚舉 for (Color col : arr) { // 查看索引 System.out.println(col + " at index " + col.ordinal()); } // 使用 valueOf() 返回枚舉常量,不存在的會報錯 IllegalArgumentException System.out.println(Color.valueOf("RED")); // System.out.println(Color.valueOf("WHITE")); } }RED at index 0 GREEN at index 1 BLUE at index 2 RED枚舉成員
枚舉跟普通類一樣可以用自己的變量、方法和構造函數,構造函數只能使用 private 訪問修飾符,所以外部無法調用。
enum Color { RED, GREEN, BLUE; // 構造函數 private Color() { System.out.println("Constructor called for : " + this.toString()); } public void colorInfo() { System.out.println("Universal Color"); } }
包(package)
為了更好地組織類,Java 提供了包機制,用於區別類名的命名空間。
包的 3 個作用如下
-
區分相同名稱的類。
-
能夠較好地管理大量的類。
-
控制訪問范圍。
定義
```java
package 包名;
```
Java 包的命名規則如下:
- 包名全部由小寫字母(多個單詞也全部小寫)。
- 如果包名包含多個層次,每個層次用“.”分割。
- 包名一般由倒置的域名開頭,比如 com.baidu,不要有 www。
- 自定義包不能 java 開頭
包導入
\\如果使用不同包中的其它類,需要使用該類的全名(包名+類名)
example.Test test = new example.Test();
\\import 包名+類名;
import example.Test;\\or
import example.*;
系統包
| 包 | 說明 |
|---|---|
| java.lang | Java 的核心類庫,包含運行 Java 程序必不可少的系統類,如基本數據類型、基本數學函數、 字符串處理、異常處理和線程類等,系統默認加載這個包 |
| java.io | Java 語言的標准輸入/輸出類庫,如基本輸入/輸出流、文件輸入/輸出、過濾輸入/輸出流等 |
| java.util | 包含如處理時間的 Date 類,處理動態數組的 Vector 類,以及 Stack 和 HashTable 類 |
| java.awt | 構建圖形用戶界面(GUI)的類庫,低級繪圖操作 Graphics 類、圖形界面組件和布局管理 (如 Checkbox 類、Container 類、LayoutManger 接口等),以及用戶界面交互控制和事 件響應(如 Event 類) |
| java.awt.image | 處理和操縱來自網上的圖片的 Java 工具類庫 |
| java.wat.peer | 很少在程序中直接用到,使得同一個 Java 程序在不同的軟硬件平台上運行 |
| java.net | 實現網絡功能的類庫有 Socket 類、ServerSocket 類 |
| java.lang.reflect | 提供用於反射對象的工具 |
| java.util.zip | 實現文件壓縮功能 |
| java.awt.datatransfer | 處理數據傳輸的工具類,包括剪貼板、字符串發送器等 |
| java.sql | 實現 JDBC 的類庫 |
| java.rmi | 提供遠程連接與載入的支持 |
| java. security | 提供安全性方面的有關支持 |
9.異常處理
異常處理的概念
是編程語言或計算機硬件里的一種機制,用於處理軟件或信息系統中出現的異常狀況(即超出程序正常執行流程的某些特殊條件)。
關鍵字
Java異常機制用到的幾個關鍵字:try、catch、finally、throw、throws。
try -- 用於監聽。將要被監聽的代碼(可能拋出異常的代碼)放在try語句塊之內,當try語句塊內發生異常
時,異常就被拋出。
catch -- 用於捕獲異常。catch用來捕獲try語句塊中發生的異常。
finally -- finally語句塊總是會被執行。它主要用於回收在try塊里打開的物力資源(如數據庫連接、網絡
連接和磁盤文件)。只有finally塊,執行完成之后,才會回來執行try或者catch塊中的return或者throw語
句,如果finally中使用了return或者throw等終止方法的語句,則就不會跳回執行,直接停止。
throw -- 用於拋出異常。
throws -- 用在方法簽名中,用於聲明該方法可能拋出的異常。
try{
可能會發生的異常
}catch(異常類型 異常名(變量)){
針對異常進行處理的代碼
}catch(異常類型 異常名(變量)){
針對異常進行處理的代碼
}...
[finally{
釋放資源代碼;
}]
Error與Exception區別
Error(錯誤)是系統中的錯誤,程序員是不能改變的和處理的,是在程序編譯時出現的錯誤,只能通過修改程序才能修正。一般是指與虛擬機相關的問題,如系統崩潰,虛擬機錯誤,內存空間不足,方法調用棧溢等。對於這類錯誤的導致的應用程序中斷,僅靠程序本身無法恢復和和預防,遇到這樣的錯誤,建議讓程序終止。
Exception(異常)表示程序可以處理的異常,可以捕獲且可能恢復。遇到這類異常,應該盡可能處理異常,使程序恢復運行,而不應該隨意終止異常。
throw與throws區別
throw:指的是在方法中人為拋出一個異常對象(這個異常對象可能是自己實例化或者拋出已存在的);
throw ThrowableInstance;
throws:在方法的聲明上使用,表示此方法在調用時必須處理異常。
throw new NullPointerException("demo");

Java異常層次結構圖(網上獲取)
10.集合框架
所有集合類都位於 java.util 包下。Java的集合類主要由兩個接口派生而出:Collection 和 Map,Collection 和 Map 是 Java 集合框架的根接口,這兩個接口又包含了一些子接口或實現類。
集合框架被設計成要滿足以下幾個目標:
- 該框架必須是高性能的。基本集合(動態數組,鏈表,樹,哈希表)的實現也必須是高效的。
- 該框架允許不同類型的集合,以類似的方式工作,具有高度的互操作性。
- 對一個集合的擴展和適應必須是簡單的。
集合框架都包含如下內容:
-
接口:是代表集合的抽象數據類型。例如 Collection、List、Set、Map 等。之所以定義多個接口,是為了以不同的方式操作集合對象
-
實現(類):是集合接口的具體實現。從本質上講,它們是可重復使用的數據結構,例如:ArrayList、LinkedList、HashSet、HashMap。
-
算法:是實現集合接口的對象里的方法執行的一些有用的計算,例如:搜索和排序。這些算法被稱為多態,那是因為相同的方法可以在相似的接口上有着不同的實現。

Collection是一個基本的集合接口,Collection中可以容納一組集合元素(Element)
Collection 接口
Collection 是最基本的集合接口,一個 Collection 代表一組 Object,即 Collection 的元素, Java不提供直接繼承自Collection的類,只提供繼承於的子接口(如List和set)。
List
List接口是一個有序, 元素可重復的 Collection,使用此接口能夠精確的控制每個元素插入的位置,能夠通過索引(元素在List中位置,類似於數組的下標)來訪問List中的元素,第一個元素的索引為 0,而且允許有相同的元素。
- ArrayList
底層數據結構是數組,查改快,增刪慢。
非線程安全,效率高
方法:

排序
import java.util.Collections; // 引入 Collections 類
Collections.sort(sites); *// 字母排序*
- Vector
底層數據結構是數組,查改快,增刪慢。
線程安全,效率低
- LinkedList
底層數據結構是鏈表,查改慢,增刪快。
非線程安全,效率高
以下情況使用 LinkedList :
- 你需要通過循環迭代來訪問列表中的某些元素。
- 需要頻繁的在列表開頭、中間、末尾等位置進行添加和刪除元素操作。
LinkedList 繼承了 AbstractSequentialList 類。
LinkedList 實現了 Queue 接口,可作為隊列使用。
LinkedList 實現了 List 接口,可進行列表的相關操作。
LinkedList 實現了 Deque 接口,可作為隊列使用。
LinkedList 實現了 Cloneable 接口,可實現克隆。
LinkedList 實現了 java.io.Serializable 接口,即可支持序列化,能通過序列化去傳輸。
方法:

Set
Set 接口存儲一組唯一,無序的對象。
- HashSet
底層數據結構是哈希表。(無序,唯一)
依賴兩個方法:hashCode()和equals() 保證元素唯一性
// 引入 HashSet 類
import java.util.HashSet;
public class RunoobTest {
public static void main(String[] args) {
HashSet<String> sites = new HashSet<String>();
sites.add("Google");
sites.add("Runoob");
sites.add("Taobao");
sites.add("Zhihu");
sites.add("Runoob"); // 重復的元素不會被添加
System.out.println(sites);
}
}
以上代碼只會輸出一個Runoob。
- LinkedHashSet
底層數據結構是鏈表和哈希表。(FIFO插入有序,唯一)
1.由鏈表保證元素有序
2.由哈希表保證元素唯一
- TreeSet
底層數據結構是紅黑樹。(唯一,有序)
如何保證元素排序的呢? 自然排序,比較器排序
Set和List的區別
- Set 接口實例存儲的是無序的,不重復的數據。List 接口實例存儲的是有序的,可以重復的元素。
- Set檢索效率低下,刪除和插入效率高,插入和刪除不會引起元素位置改變 <實現類有HashSet,TreeSet>。
- List和數組類似,可以動態增長,根據實際存儲的數據的長度自動增長List的長度。查找元素效率高,插入刪除效率低,因為會引起其他元素位置改變 <實現類有ArrayList,LinkedList,Vector> 。

Map與Collection是並列關系。Map提供鍵(key)到值(value)的映射。一個Map中不能包含相同的鍵,每個鍵只能映射一個值。
- HashMap
無序,非線程安全,效率高。HashMap允許null值(key和value都允許)。

- HashTable
無序,線程安全,效率低。除構造函數外,HashTable的所有 public 方法聲明中都有 synchronized關鍵字,而HashMap的源碼中則沒有。HashTable不允許null值(key和value都允許)。
- TreeMap
有序,非線程安全,效率高(O(logN)),但比不上HashMap (O(1))。
11.流(Stream)、文件(File)和IO
Java.io 包中定義了多個流類型(類或抽象類)來實現輸入/輸出功能;

可以從不同的角度對其進行分
類:
1.按數據流的方向不同可以分為輸入流【InputStream(字節流),Reader(字符流)】和輸出流【OutPutStream(字節流),Writer(字符流)】
2.按照處理數據單位不同可以分為字節流【一個字節(Byte)是8位(bit))】和字符流【一個字符是2個字節】
3.按照功能不同可以分為節點流和處理流

4.按照操作對象分

InputStream 和 OutputStream


import java.io.*;
public class fileStreamTest {
public static void main(String[] args) {
try {
byte bWrite[] = { 11, 21, 3, 40, 5 };
OutputStream os = new FileOutputStream("test.txt");
for (int x = 0; x < bWrite.length; x++) {
os.write(bWrite[x]); // writes the bytes
}
os.close();
InputStream is = new FileInputStream("test.txt");
int size = is.available();
for (int i = 0; i < size; i++) {
System.out.print((char) is.read() + " ");
}
is.close();
} catch (IOException e) {
System.out.print("Exception");
}
}
}
上面的程序首先創建文件test.txt,並把給定的數字以二進制形式寫進該文件,同時輸出到控制台上。
以上代碼由於是二進制寫入,可能存在亂碼,你可以使用以下代碼實例來解決亂碼問題:
//文件名 :fileStreamTest2.java
import java.io.*;
public class fileStreamTest2 {
public static void main(String[] args) throws IOException {
File f = new File("a.txt");
FileOutputStream fop = new FileOutputStream(f);
// 構建FileOutputStream對象,文件不存在會自動新建
OutputStreamWriter writer = new OutputStreamWriter(fop, "UTF-8");
// 構建OutputStreamWriter對象,參數可以指定編碼,默認為操作系統默認編碼,windows上是gbk
writer.append("中文輸入");
// 寫入到緩沖區
writer.append("\r\n");
// 換行
writer.append("English");
// 刷新緩存沖,寫入到文件,如果下面已經沒有寫入的內容了,直接close也會寫入
writer.close();
// 關閉寫入流,同時會把緩沖區內容寫入文件,所以上面的注釋掉
fop.close();
// 關閉輸出流,釋放系統資源
FileInputStream fip = new FileInputStream(f);
// 構建FileInputStream對象
InputStreamReader reader = new InputStreamReader(fip, "UTF-8");
// 構建InputStreamReader對象,編碼與寫入相同
StringBuffer sb = new StringBuffer();
while (reader.ready()) {
sb.append((char) reader.read());
// 轉成char加到StringBuffer對象中
}
System.out.println(sb.toString());
reader.close();
// 關閉讀取流
fip.close();
// 關閉輸入流,釋放系統資源
}
}
Reader 流與Writer流


Reader ,Write與InputStream ,OutputStream: 唯一的區別就在於讀的數據單位不同分別為(16bit),(8bit)
創建讀取目錄:
import java.io.File;
public class CreateDir {
public static void main(String[] args) {
String dirname = "/tmp/user/java/bin";
File d = new File(dirname);
// 現在創建目錄
d.mkdirs();
}
}
import java.io.File;
public class DirList {
public static void main(String args[]) {
String dirname = "/tmp";
File f1 = new File(dirname);
if (f1.isDirectory()) {
System.out.println("目錄 " + dirname);
String s[] = f1.list();
for (int i = 0; i < s.length; i++) {
File f = new File(dirname + "/" + s[i]);
if (f.isDirectory()) {
System.out.println(s[i] + " 是一個目錄");
} else {
System.out.println(s[i] + " 是一個文件");
}
}
} else {
System.out.println(dirname + " 不是一個目錄");
}
}
}
刪除
import java.io.File;
public class DeleteFileDemo {
public static void main(String[] args) {
// 這里修改為自己的測試目錄
File folder = new File("/tmp/java/");
deleteFolder(folder);
}
// 刪除文件及目錄
public static void deleteFolder(File folder) {
File[] files = folder.listFiles();
if (files != null) {
for (File f : files) {
if (f.isDirectory()) {
deleteFolder(f);
} else {
f.delete();
}
}
}
folder.delete();
}
}
緩存流
是處理流的一種,它是要“套接”在相應的節點流之上,對讀寫的數據提供了緩沖的功能,避免頻繁讀寫硬盤, 提高了讀寫的效率。同時增加了一些新的方法。
BufferedReader(Reader in)
BufferedReader(Reader in,int sz) //sz 為自定義緩沖區的大小
BufferedWriter(Writer out)
BufferedWriter(Writer out,int sz)
BufferedInputStream(InputStream in)
BufferedInputStream(InputStream in,int size)
BufferedOutputStream(InputStream in)
BufferedOutputStream(InputStream in,int size)
BufferedInputStream
package com.kuang.chapter;
import java.io.*;
public class TestBufferStream {
public static void main(String args[]) {
FileInputStream fis = null;
File f = new File("a.txt");
try {
fis = new FileInputStream( f);
// 在FileInputStream節點流的外面套接一層處理流BufferedInputStream
BufferedInputStream bis = new BufferedInputStream(fis);
int c = 0;
System.out.println((char) bis.read());
System.out.println((char) bis.read());
bis.mark(100);// 在第100個字符處做一個標記
for (int i = 0; i <= 10 && (c = bis.read()) != -1; i++) {
System.out.print((char) c);
}
System.out.println();
bis.reset();// 重新回到原來標記的地方
for (int i = 0; i <= 10 && (c = bis.read()) != -1; i++) {
System.out.print((char) c);
}
bis.close();
} catch (FileNotFoundException e) {
e.printStackTrace();
} catch (Exception e1) {
e1.printStackTrace();
}
}
}
BufferedReader
package com.kuang.chapter;
import java.io.*;
public class TestBufferStream{
public static void main(String args[]){
try{
BufferedWriter bw = new BufferedWriter(new FileWriter("a\\Student.txt"));
//在節點流FileWriter的外面再套一層處理流BufferedWriter
String s = null;
for(int i=0;i<100;i++){
s = String.valueOf(Math.random());//“Math.random()”將會生成一系列介於0~1之間的隨機數。
// static String valueOf(double d)這個valueOf()方法的作用就是把
一個double類型的數轉換成字符串
//valueOf()是一個靜態方法,所以可以使用“類型.靜態方法名”的形式來調用
bw.write(s);//把隨機數字符串寫入到指定文件中
bw.newLine();//調用newLine()方法使得每寫入一個隨機數就換行顯示
}
bw.flush();//調用flush()方法清空緩沖區
BufferedReader br = new BufferedReader(new FileReader("a:\\Student.txt"));
//在節點流FileReader的外面再套一層處理流BufferedReader
while((s = br.readLine())!=null){
//使用BufferedReader處理流里面提供String readLine()方法讀取文件中的數據時是一行一行讀取的
//循環結束的條件就是使用readLine()方法讀取數據返回的字符串為空值后則表
示已經讀取到文件的末尾了。
System.out.println(s);
}
bw.close();
br.close();
}catch(Exception e){
e.printStackTrace();
}
}
}
轉換流
InputStreamReader 和 OutputStreamWriter 用於字節數據到字符數據之間的轉換
InputStreamReader 需要和 InputStream “套接” 。
OutputStreamWriter 需要和 OutputStream “套接” 。
轉換流在構造時可以指定其編碼集合
import java.io.*;
public class TestTransform1 {
public static void main(String args[]) {
try {
OutputStreamWriter osw = new OutputStreamWriter(new FileOutputStream("D:/char.txt"));
osw.write("熊方園真煩人");// 把字符串寫入到指定的文件中去
System.out.println(osw.getEncoding());// 使用getEncoding()方法取得當前系統的默認字符編碼
osw.close();
osw = new OutputStreamWriter(new FileOutputStream("D:\\java\\char.txt", true), "utf-8");// 如果在調用FileOutputStream的構造方法時沒有加入true,那么新加入的字符 串就會替換掉原來寫入的字符串,在調用構造方法時指定了字符的編碼
osw.write("不想搭理她");// 再次向指定的文件寫入字符串,新寫入的字符串加入到原來字符串的后面
System.out.println(osw.getEncoding());
osw.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
數據流
數據流 DataInputStream DataOutputStream 【分別繼承自InputStream 和 OutputStream】等-提供將基礎數據類型寫入到文件中,或者讀取出來.提供了可以存取與機器無關的Java原始類型數據(int,double等)的方法。
public static void main(String args[]){
ByteArrayOutputStream baos = new ByteArrayOutputStream();
//在調用構造方法時,首先會在內存里面創建一個ByteArray字節數組
DataOutputStream dos = new DataOutputStream(baos);
//在輸出流的外面套上一層數據流,用來處理int,double類型的數
try{
dos.writeDouble(Math.random());//把產生的隨機數直接寫入到字節數組
ByteArray中
dos.writeBoolean(true);//布爾類型的數據在內存中就只占一個字節
ByteArrayInputStream bais = new
ByteArrayInputStream(baos.toByteArray());
System.out.println(bais.available());
DataInputStream dis = new DataInputStream(bais);
System.out.println(dis.readDouble());//先寫進去的就先讀出來,調用readDouble()方法讀取出寫入的隨機數
System.out.println(dis.readBoolean());//后寫進去的就后讀出來,這里面的讀取順序不能更改位置,否則會打印出不正確的結果
dos.close();
bais.close();
}catch(Exception e){
e.printStackTrace();
}
}
打印流
打印流是輸出信息最方便的類,注意包含字節打印流PrintStream和字符打印流:PrintWriter。打印流提供了非常方便的打印功能,
可以打印任何類型的數據信息,例如:小數,整數,字符串。
對象流
對象的輸入輸出流的作用: 用於寫入對象 的信息和讀取對象的信息。 使得對象持久化。
ObjectInputStream : 對象輸入流
ObjectOutPutStream :對象輸出流
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
//創建要寫入磁盤的類,這個類需要實現接口 Serializable(可系列化的)
class Student implements Serializable{
// 在這里保證了serialVersionUID 的唯一性,防止屬性變量的臨時改變,從而造成寫入id與讀取id不同
private static final long serialVersionUID = 1L;
int id ; //額外需要添加一個屬性
String name ;
transient String sex; //transient修飾屬性,表示暫時的,則這個屬性不會被寫入磁盤
transient int age;
public Student(String name,String sex,int age){
this.name = name;
this.sex = sex;
this.age = age;
}
}
public class objectIO {
/**
* @param args
* @throws IOException
* @throws ClassNotFoundException
*/
public static void main(String[] args) throws IOException, ClassNotFoundException {
// TODO Auto-generated method stub
createObj();
readObj();
}
//(一)先寫入對象
public static void createObj() throws IOException {
//1.創建目標路徑
File file = new File("C:\\Users\\bg\\Desktop\\objTest.txt");
//2.創建流通道
FileOutputStream fos = new FileOutputStream(file);
//3.創建對象輸出流
ObjectOutputStream objOP = new ObjectOutputStream(fos);
//4.創建類對象,並初始化
Student stu = new Student("瑪麗蘇", "男", 18);
//5.向目標路徑文件寫入對象
objOP.writeObject(stu);
//6.關閉資源
objOP.close();
}
//再讀取對象
public static void readObj() throws IOException, ClassNotFoundException {
File file = new File("C:\\Users\\bg\\Desktop\\objTest.txt");
FileInputStream fis = new FileInputStream(file);
ObjectInputStream objIP = new ObjectInputStream(fis);
//讀取對象數據,需要將對象流強制轉換為 要寫入對象的類型
Student stu = (Student)objIP.readObject();
System.out.println("\n name:"+stu.name+"\n sex:"+stu.sex+"\n age:"+stu.age);
objIP.close();
}
}
流的關閉順序
- 一般情況下是:先打開的后關閉,后打開的先關閉
- 另一種情況:看依賴關系,如果流a依賴流b,應該先關閉流a,再關閉流b。例如,處理流a依賴節點流b,應該先關閉處理流a,再關閉節點流b
- 可以只關閉處理流,不用關閉節點流。處理流關閉的時候,會調用其處理的節點流的關閉方法。
12.多線程
進程與線程
線程的創建
繼承Thread類,實現Runnable接口,實現Callable接口
1.繼承Thread類
public class ThreadCreateDemo1 {
public static void main(String[] args) {
MyThread thread = new MyThread();
thread.start(); //調用start()方法啟動線程,線程不一定立即執行,CPU安排調度
}
}
class MyThread extends Thread {//繼承Thread類
@Override
public void run() {//重寫run()方法,編寫線程執行體
super.run();
System.out.println("hellow_world!");
}
}
2.實現Runnable接口
public class ThreadCreateDemo2 {
//創建線程對象,調用start()方法啟動線程
public static void main(String[] args) {
Runnable runnable = new MyRunnable();
Thread thread = new Thread(runnable);
thread.start();
}
}
class MyRunnable implements Runnable {
public void run() {
System.out.println("通過Runnable創建的線程!");
}
}
上述兩種創建方式,工作時性質一樣。但是建議使用*實現Runable接口*方式。解決單繼承的局限性。
3.實現Callable接口
public class ThreadCreateDemo3 implements Callable<Integer>{
// 實現call方法,作為線程執行體
public Integer call(){
int i = 0;
for ( ; i < 100 ; i++ ){
System.out.println(Thread.currentThread().getName()+ "\t" + i);
}
// call()方法可以有返回值
return i;
}
public static void main(String[] args) {
// 創建Callable對象
ThreadCreateDemo3 myCallableTest = new ThreadCreateDemo3();
// 使用FutureTask來包裝Callable對象
FutureTask<Integer> task = new FutureTask<Integer>(myCallableTest);
for (int i = 0 ; i < 100 ; i++){
System.out.println(Thread.currentThread().getName()+ " \t" + i);
if (i == 20){
// 實質還是以Callable對象來創建、並啟動線程
new Thread(task , "callable").start();
}
}
try{
// 獲取線程返回值
System.out.println("callable返回值:" + task.get());
}
catch (Exception ex){
ex.printStackTrace();
}
}
}
-
實現Callable接口,需要返回值類型
-
重寫call方法,需要拋出異常
-
創建目標對象
-
創建執行服務:ExecutorService ser = Executors.newFixedThreadPool(1);
-
提交執行:Future
result1 = ser.submit(t1); -
獲取結果:boolean r1 = result1.get()
-
關閉服務:ser.shutdownNow();
總結 -
不過實現Runnable接口與實現Callable接口的方式基本相同,只是Callable接口里定義的方法有返回值,可以聲明拋出異常而已。 因此可以將實現Runnable接口和實現Callable接口歸為一種方式。
-
Runnable、Callable接口的方式創建多線程,所以非常適合多個相同線程來處理同一份資源的情況,如果需要訪問當前線程,則必須使用Thread.currentThread()方法
-
采用繼承Thread類的方式創建多線程,因為線程類已經繼承了Thread類,所以不能再繼承其他父類
生命周期
線程被創建並啟動以后,它既不是一啟動就進入了執行狀態,也不是一直處於執行狀態新建(New)、就緒(Runnable)、運行(Running)、阻塞(Blocked)和死亡(Dead)5種狀態


Thread.State:
-
初始(NEW):新創建了一個線程對象,但還沒有調用start()方法。
-
運行(RUNNABLE):Java線程中將就緒(ready)和運行中(running)兩種狀態籠統的稱為“運行”。
線程對象創建后,其他線程(比如main線程)調用了該對象的start()方法。該狀態的線程位於可運行線程池中,等待被線程調度選中,獲取CPU的使用權,此時處於就緒狀態(ready)。就緒狀態的線程在獲得CPU時間片后變為運行中狀態(running)。 -
阻塞(BLOCKED):表示線程阻塞於鎖。
-
等待(WAITING):進入該狀態的線程需要等待其他線程做出一些特定動作(通知或中斷)。
-
超時等待(TIMED_WAITING):該狀態不同於WAITING,它可以在指定的時間后自行返回。
-
終止(TERMINATED):表示該線程已經執行完畢
線程的優先級
Java提供一個線程調度器來監控程序中啟動后進入就緒狀態的所有線程,線程調度
器按照優先級決定應該調度哪個線程來執行。
線程的優先級用數字表示,范圍從1~10.
hread.MIN_PRIORITY = 1;
Thread.MAX_PRIORITY = 10;
Thread.NORM_PRIORITY = 5;
使用以下方式改變或獲取優先級
getPriority() . setPriority(int xxx)
線程方法
| 1 | public void start() 使該線程開始執行;Java 虛擬機調用該線程的 run 方法。 |
|---|---|
| 2 | public void run() 如果該線程是使用獨立的 Runnable 運行對象構造的,則調用該 Runnable 對象的 run 方法;否則,該方法不執行任何操作並返回。 |
| 3 | public final void setName(String name) 改變線程名稱,使之與參數 name 相同。 |
| 4 | public final void setPriority(int priority) 更改線程的優先級。 |
| 5 | public final void setDaemon(boolean on) 將該線程標記為守護線程或用戶線程。 |
| 6 | public final void join(long millisec) 等待該線程終止的時間最長為 millis 毫秒。 |
| 7 | public void interrupt() 中斷線程。 |
| 8 | public final boolean isAlive() 測試線程是否處於活動狀態。 |
| 9 | public static void yield() 線程禮讓: 暫停當前正在執行的線程對象,並執行其他線程。 |
| 10 | public static void sleep(long millisec) 線程休眠: 在指定的毫秒數內讓當前正在執行的線程休眠(暫停執行),此操作受到系統計時器和調度程序精度和准確性的影響。 |
| 11 | public static boolean holdsLock(Object x) 當且僅當當前線程在指定的對象上保持監視器鎖時,才返回 true。 |
| 12 | public static Thread currentThread() 返回對當前正在執行的線程對象的引用。 |
| 13 | public static void dumpStack() 將當前線程的堆棧跟蹤打印至標准錯誤流。 |
停止線程:jdk提供了stop,但不建議使用可以自己去停止它
守護(daemon)線程
線程分為前台線程與后台線程(用戶線程與守護線程)
虛擬機必須確保用戶線程執行完畢
虛擬機不用等待守護線程執行完畢
並發,隊列 和 鎖,死鎖
同一個對象被多個線程同時操作就是並發。
多個線程訪問同一個對象, 並且某些線程還想修改這個對象 .這時候我們就需要線程同步 . 線程同步其實就是一種等待機制 , 多個需要同時訪問此對象的線程進入這個對象的等待池 形成隊列, 等待前面線程使用完畢 , 下一個線
程再使用。
上面的並發問題我們會加一個鎖(synchronized)來解決。我鎖上門的時候你們都別進來。但是加上鎖之后會有以下為:
-
一個線程持有鎖會導致其他所有需要此鎖的線程掛起 ;
-
在多線程競爭下 , 加鎖 , 釋放鎖會導致比較多的上下文切換 和 調度延時,引起性能問題 ;
-
如果一個優先級高的線程等待一個優先級低的線程釋放鎖 會導致優先級倒置 , 引起性能問題 .
java 死鎖產生的四個必要條件:
-
1、互斥使用,即當資源被一個線程使用(占有)時,別的線程不能使用
-
2、不可搶占,資源請求者不能強制從資源占有者手中奪取資源,資源只能由資源占有者主動釋放。
-
3、請求和保持,即當資源請求者在請求其他的資源的同時保持對原有資源的占有。
-
4、循環等待,即存在一個等待隊列:P1占有P2的資源,P2占有P3的資源,P3占有P1的資源。這樣就形成了一個等待環路。
死鎖的情況下如果打破上述任何一個條件,便可讓死鎖消失。
import java.util.Date;
public class LockTest {
public static String obj1 = "obj1";
public static String obj2 = "obj2";
public static void main(String[] args) {
LockA la = new LockA();
new Thread(la).start();
LockB lb = new LockB();
new Thread(lb).start();
}
}
class LockA implements Runnable{
public void run() {
try {
System.out.println(new Date().toString() + " LockA 開始執行");
while(true){
synchronized (LockTest.obj1) {
System.out.println(new Date().toString() + " LockA 鎖住 obj1");
Thread.sleep(3000); // 此處等待是給B能鎖住機會
synchronized (LockTest.obj2) {
System.out.println(new Date().toString() + " LockA 鎖住 obj2");
Thread.sleep(60 * 1000); // 為測試,占用了就不放
}
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
class LockB implements Runnable{
public void run() {
try {
System.out.println(new Date().toString() + " LockB 開始執行");
while(true){
synchronized (LockTest.obj2) {
System.out.println(new Date().toString() + " LockB 鎖住 obj2");
Thread.sleep(3000); // 此處等待是給A能鎖住機會
synchronized (LockTest.obj1) {
System.out.println(new Date().toString() + " LockB 鎖住 obj1");
Thread.sleep(60 * 1000); // 為測試,占用了就不放
}
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
結果
Tue May 05 10:51:06 CST 2015 LockB 開始執行
Tue May 05 10:51:06 CST 2015 LockA 開始執行
Tue May 05 10:51:06 CST 2015 LockB 鎖住 obj2
Tue May 05 10:51:06 CST 2015 LockA 鎖住 obj1
解決
import java.util.Date;
import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit;
public class UnLockTest {
public static String obj1 = "obj1";
public static final Semaphore a1 = new Semaphore(1);
public static String obj2 = "obj2";
public static final Semaphore a2 = new Semaphore(1);
public static void main(String[] args) {
LockAa la = new LockAa();
new Thread(la).start();
LockBb lb = new LockBb();
new Thread(lb).start();
}
}
class LockAa implements Runnable {
public void run() {
try {
System.out.println(new Date().toString() + " LockA 開始執行");
while (true) {
if (UnLockTest.a1.tryAcquire(1, TimeUnit.SECONDS)) {
System.out.println(new Date().toString() + " LockA 鎖住 obj1");
if (UnLockTest.a2.tryAcquire(1, TimeUnit.SECONDS)) {
System.out.println(new Date().toString() + " LockA 鎖住 obj2");
Thread.sleep(60 * 1000); // do something
}else{
System.out.println(new Date().toString() + "LockA 鎖 obj2 失敗");
}
}else{
System.out.println(new Date().toString() + "LockA 鎖 obj1 失敗");
}
UnLockTest.a1.release(); // 釋放
UnLockTest.a2.release();
Thread.sleep(1000); // 馬上進行嘗試,現實情況下do something是不確定的
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
class LockBb implements Runnable {
public void run() {
try {
System.out.println(new Date().toString() + " LockB 開始執行");
while (true) {
if (UnLockTest.a2.tryAcquire(1, TimeUnit.SECONDS)) {
System.out.println(new Date().toString() + " LockB 鎖住 obj2");
if (UnLockTest.a1.tryAcquire(1, TimeUnit.SECONDS)) {
System.out.println(new Date().toString() + " LockB 鎖住 obj1");
Thread.sleep(60 * 1000); // do something
}else{
System.out.println(new Date().toString() + "LockB 鎖 obj1 失敗");
}
}else{
System.out.println(new Date().toString() + "LockB 鎖 obj2 失敗");
}
UnLockTest.a1.release(); // 釋放
UnLockTest.a2.release();
Thread.sleep(10 * 1000); // 這里只是為了演示,所以tryAcquire只用1秒,而且B要給A讓出能執行的時間,否則兩個永遠是死鎖
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
Tue May 05 10:59:13 CST 2015 LockA 開始執行
Tue May 05 10:59:13 CST 2015 LockB 開始執行
Tue May 05 10:59:13 CST 2015 LockB 鎖住 obj2
Tue May 05 10:59:13 CST 2015 LockA 鎖住 obj1
Tue May 05 10:59:14 CST 2015LockB 鎖 obj1 失敗
Tue May 05 10:59:14 CST 2015LockA 鎖 obj2 失敗
Tue May 05 10:59:15 CST 2015 LockA 鎖住 obj1
Tue May 05 10:59:15 CST 2015 LockA 鎖住 obj2
- synchronized 與 Lock 的對比
Lock是顯式鎖(手動開啟和關閉鎖,別忘記關閉鎖)synchronized是隱式鎖,出了作用域自動釋放 - Lock只有代碼塊鎖,synchronized有代碼塊鎖和方法鎖使用Lock鎖,JVM將花費較少的時間來調度線程,性能更好。並且具有更好的擴展性(提供更多的子類)
- 優先使用順序:
Lock > 同步代碼塊(已經進入了方法體,分配了相應資源)> 同步方法(在方
法體之外)
線程通訊
線程通信的目標是使線程間能夠互相發送信號。另一方面,線程通信使線程能夠等待其他線程的信號。
線程的通信方式
- volatile
- Wait/Notify機制
- join方式
- threadLocal
- CountDownLatch 並發工具
- CyclicBarrier 並發工具
volatile
public class Volatile implements Runnable {
private static volatile Boolean flag = true;
@Override
public void run() {
while (flag) {
System.out.println(Thread.currentThread().getName() + " - 執行");
}
System.out.println("線程結束");
}
public static void main(String[] args) {
Thread t = new Thread(new Volatile());
t.start();
try {
Thread.sleep(5);
flag = false;
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
Thread-0 - 執行
Thread-0 - 執行
Thread-0 - 執行
Thread-0 - 執行
Thread-0 - 執行
線程結束
**WaitNotify **
public class WaitNotify {
// 狀態鎖
private static Object lock = new Object();
private static Integer i = 0;
public void odd() {
while (i < 10) {
synchronized (lock) {
if (i % 2 == 1) {
System.out.println(Thread.currentThread().getName() + " - " + i);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
i++;
lock.notify();
} else {
try {
lock.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
public void even() {
while (i < 10) {
synchronized (lock) {
if (i % 2 == 0) {
System.out.println(Thread.currentThread().getName() + " - " + i);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
i++;
lock.notify();
} else {
try {
lock.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
public static void main(String[] args) {
WaitNotify waitNotify = new WaitNotify();
Thread t1 = new Thread(() -> waitNotify.odd(), "線程1");
Thread t2 = new Thread(() -> waitNotify.even(), "線程2");
t1.start();
t2.start();
}
}
join
package threadCommunication;
public class JoinTest extends Thread {
@Override
public void run() {
try {
int sleepTime = (int) (Math.random() * 1000);
System.out.println(sleepTime);
Thread.sleep(sleepTime);
System.out.println("JoinTest end");
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public static void main(String[] args) throws InterruptedException {
JoinTest j = new JoinTest();
j.start();
j.join();//當前線程main等待線程對象(j)銷毀
System.out.println("main end");
}
threadLocal
package sync;
public class SequenceNumber {
// 定義匿名子類創建ThreadLocal的變量
private static ThreadLocal<Integer> seqNum = new ThreadLocal<Integer>() {
// 覆蓋初始化方法
public Integer initialValue() {
return 0;
}
};
// 下一個序列號
public int getNextNum() {
seqNum.set(seqNum.get() + 1);
return seqNum.get();
}
private static class TestClient extends Thread {
private SequenceNumber sn;
public TestClient(SequenceNumber sn) {
this.sn = sn;
}
// 線程產生序列號
public void run() {
for (int i = 0; i < 3; i++) {
System.out.println("thread[" + Thread.currentThread().getName() + "] sn[" + sn.getNextNum() + "]");
}
}
}
/**
* @param args
*/
public static void main(String[] args) {
SequenceNumber sn = new SequenceNumber();
// 三個線程產生各自的序列號
TestClient t1 = new TestClient(sn);
TestClient t2 = new TestClient(sn);
TestClient t3 = new TestClient(sn);
t1.start();
t2.start();
t3.start();
}
}
thread[Thread-1] sn[1]
thread[Thread-1] sn[2]
thread[Thread-1] sn[3]
thread[Thread-2] sn[1]
thread[Thread-2] sn[2]
thread[Thread-2] sn[3]
thread[Thread-0] sn[1]
thread[Thread-0] sn[2]
thread[Thread-0] sn[3]
**CountDownLatch **CountDownLatch可以代替wait/notify的使用,並去掉synchronized
import java.util.concurrent.CountDownLatch;
public class CountDown {
private static Integer i = 0;
final static CountDownLatch countDown = new CountDownLatch(1);
public void odd() {
while (i < 10) {
if (i % 2 == 1) {
System.out.println(Thread.currentThread().getName() + " - " + i);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
i++;
countDown.countDown();
} else {
try {
countDown.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
public void even() {
while (i < 10) {
if (i % 2 == 0) {
System.out.println(Thread.currentThread().getName() + " - " + i);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
i++;
countDown.countDown();
} else {
try {
countDown.await();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
public static void main(String[] args) {
CountDown countDown = new CountDown();
Thread t1 = new Thread(() -> countDown.odd(), "線程1");
Thread t2 = new Thread(() -> countDown.even(), "線程2");
t1.start();
t2.start();
}
}
CyclicBarrier
import java.util.concurrent.BrokenBarrierException;
import java.util.concurrent.CyclicBarrier;
public class CyclicBarrierTest {
public static void main(String[] args) {
CyclicBarrier cyclicBarrier = new CyclicBarrier(3);
new Thread(() -> {
System.out.println(Thread.currentThread().getName() + ": 准備...");
try {
cyclicBarrier.await();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (BrokenBarrierException e) {
e.printStackTrace();
}
System.out.println("全部啟動完畢!");
}, "線程1").start();
new Thread(() -> {
System.out.println(Thread.currentThread().getName() + ": 准備...");
try {
cyclicBarrier.await();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (BrokenBarrierException e) {
e.printStackTrace();
}
System.out.println("全部啟動完畢!");
}, "線程2").start();
new Thread(() -> {
System.out.println(Thread.currentThread().getName() + ": 准備...");
try {
cyclicBarrier.await();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (BrokenBarrierException e) {
e.printStackTrace();
}
System.out.println("全部啟動完畢!");
}, "線程3").start();
}
}
線程3: 准備...
線程2: 准備...
線程1: 准備...
全部啟動完畢!
全部啟動完畢!
全部啟動完畢!
線程池
經常創建和銷毀、使用量特別大的資源,比如並發情況下的線程,對性能影響很大。提前創建好多個線程,放入線程池中,使用時直接獲取,使用完放回池中。可以避免頻繁創建銷毀、實現重復利用。
ExecutorService 和 Executors
-
ExecutorService:真正的線程池接口。常見子類ThreadPoolExecutor
void execute(Runnable command) :執行任務/命令,沒有返回值,一般用來執
行Runnable -
Future submit(Callable task):執行任務,有返回值,一般又來執行
Callable -
void shutdown() :關閉連接池
Executors:工具類、線程池的工廠類,用於創建並返回不同類型的線程池
13.注解
Java 注解(Annotation)又稱 Java 標注,是 JDK5.0 引入的一種注釋機制。
作用
不是程序本身 , 可以對程序作出解釋.(這一點和注釋(comment)沒什么區別)
可以被其他程序(比如:編譯器等)讀取.
可以附加在package , class , method , field 等上面 , 相當於給他們添加了額外的輔助信息
我們可以通過反射機制實現對這些元數據的訪問
格式
注解是以"@注釋名"在代碼中存在的
還可以添加一些參數值 , 例如:@SuppressWarnings(value="unchecked")
內置注解

Java 定義了一套注解,共有 10 個,java7之前3 個在 java.lang 中, 4 個在 java.lang.annotation 中,后續增加三個
作用在代碼的注解( java.lang )是
@Override - 檢查該方法是否是重寫方法。如果發現其父類,或者是引用的接口中並沒有該方法時,會報編譯錯誤。
@Deprecated - 標記過時方法。如果使用該方法,會報編譯警告。
@SuppressWarnings - 指示編譯器去忽略注解中聲明的警告。
作用在其他注解的注解(或者說 元注解)是( java.lang.annotation)注解
@Retention - 標識這個注解怎么保存,是只在代碼中,還是編入class文件中,或者是在運行時可以通過反射訪問。
@Documented - 標記這些注解是否包含在用戶文檔中。
@Target - 標記這個注解應該是哪種 Java 成員。
@Inherited - 標記這個注解是繼承於哪個注解類(默認 注解並沒有繼承於任何子類)
java7之后增加的注解
@SafeVarargs - Java 7 開始支持,忽略任何使用參數為泛型變量的方法或構造函數調用產生的警告。
@FunctionalInterface - Java 8 開始支持,標識一個匿名函數或函數式接口。
@Repeatable - Java 8 開始支持,標識某注解可以在同一個聲明上使用多次。
package com.annotation;
//測試內置注解
import java.util.ArrayList;
import java.util.List;
//所有類默認繼承Object類
public class Test1 extends Object {
//@Override 表示方法重寫
//--> 查看JDK幫助文檔
//--> 測試名字不同產生的效果
@Override
public String toString() {
return super.toString();
}
//方法過時了, 不建議使用 , 可能存在問題 , 並不是不能使用!
//--> 查看JDK幫助文檔
@Deprecated
public static void stop(){
System.out.println("測試 @Deprecated");
}
//@SuppressWarnings 抑制警告 , 可以傳參數
//--> 查看JDK幫助文檔
//查看源碼:發現 參數類型 和 參數名稱 , 並不是方法!
@SuppressWarnings("all")
public void sw(){
List list = new ArrayList();
}
public static void main(String[] args) {
stop();
}
}
元注解
package com.annotation;
import java.lang.annotation.*;
//測試元注解
public class Test2 {
@MyAnnotation
public void test(){
}
}
//定義一個注解
@Target(value = {ElementType.METHOD,ElementType.TYPE})
@Retention(value = RetentionPolicy.RUNTIME)
@Inherited
@Documented
@interface MyAnnotation{
}
自定義注解
-
使用 @interface自定義注解時 , 自動繼承了java.lang.annotation.Annotation接口
@ interface用來聲明一個注解 , 格式 : public @ interface 注解名 { 定義內容 } -
其中的每一個方法實際上是聲明了一個配置參數.
方法的名稱就是參數的名稱. -
返回值類型就是參數的類型 ( 返回值只能是基本類型,Class , String , enum ).
可以通過default來聲明參數的默認值 -
如果只有一個參數成員 , 一般參數名為value
-
注解元素必須要有值 , 我們定義注解元素時 , 經常使用空字符串,0作為默認值 .
注解參數的可支持數據類型:
1.所有基本數據類型(int,float,boolean,byte,double,char,long,short)
2.String類型
3.Class類型
4.enum類型
5.Annotation類型
6.以上所有類型的數組
package annotation;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* 水果名稱注解
* @author peida
*
*/
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface FruitName {
String value() default "";
}
package annotation;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* 水果顏色注解
* @author peida
*
*/
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface FruitColor {
/**
* 顏色枚舉
* @author peida
*
*/
public enum Color{ BULE,RED,GREEN};
/**
* 顏色屬性
* @return
*/
Color fruitColor() default Color.GREEN;
}
package annotation;
import annotation.FruitColor.Color;
public class Apple {
@FruitName("Apple")
private String appleName;
@FruitColor(fruitColor=Color.RED)
private String appleColor;
public void setAppleColor(String appleColor) {
this.appleColor = appleColor;
}
public String getAppleColor() {
return appleColor;
}
public void setAppleName(String appleName) {
this.appleName = appleName;
}
public String getAppleName() {
return appleName;
}
public void displayName(){
System.out.println("水果的名字是:蘋果");
}
}
//設置默認值
package annotation;
import java.lang.annotation.Documented;
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* 水果供應者注解
* @author peida
*
*/
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
@Documented
public @interface FruitProvider {
/**
* 供應商編號
* @return
*/
public int id() default -1;
/**
* 供應商名稱
* @return
*/
public String name() default "";
/**
* 供應商地址
* @return
*/
public String address() default "";
}
14.反射
反射機制

Java的反射機制的實現要借助於4個類:class,Constructor,Field,Method;
其中class代表的時類對 象,Constructor-類的構造器對象,Field-類的屬性對象,Method-類的方法對象。通過這四個對象我們可以粗略的看到一個類的各個組 成部分。
獲取類的方法
//調用運行時類本身的.class屬性
Class clazz = String.class;
//通過運行時類的對象獲取
Person p = new Person();
Class clazz = p.getClass();
//通過Class的靜態方法獲取:體現反射的動態性
String className = “java.util.commons”;
Class clazz = Class.forName(className);
//通過類的加載器
String className = “java.util.commons”;
ClassLoader classLoader = this.getClass().getClassLoader();
Class clazz = classLoader.loadClass(className);
得到構造器的方法
Constructor getConstructor(Class[] params) -- 獲得使用特殊的參數類型的公共構造函數,
Constructor[] getConstructors() -- 獲得類的所有公共構造函數
Constructor getDeclaredConstructor(Class[] params) -- 獲得使用特定參數類型的構造函數(與接入級別無關)
Constructor[] getDeclaredConstructors() -- 獲得類的所有構造函數(與接入級別無關)
獲取字段
Field getField(String name) -- 獲得命名的公共字段
Field[] getFields() -- 獲得類的所有公共字段
Field getDeclaredField(String name) -- 獲得類聲明的命名的字段
Field[] getDeclaredFields() -- 獲得類聲明的所有字段
獲取方法的信息
Method getMethod(String name, Class[] params) -- 使用特定的參數類型,獲得命名的公共方法
Method[] getMethods() -- 獲得類的所有公共方法
Method getDeclaredMethod(String name, Class[] params) -- 使用特寫的參數類型,獲得類聲明的命名的方法
Method[] getDeclaredMethods() -- 獲得類聲明的所有方法
通過 Class 類獲取成員變量、成員方法、接口、超類、構造方法等
package com.ys.reflex;
public class Person {
//私有屬性
private String name = "Tom";
//公有屬性
public int age = 18;
//構造方法
public Person() {
}
//私有方法
private void say(){
System.out.println("private say()...");
}
//公有方法
public void work(){
System.out.println("public work()...");
}
}
//獲得類完整的名字
String className = c2.getName();
System.out.println(className);//輸出com.ys.reflex.Person
//獲得類的public類型的屬性。
Field[] fields = c2.getFields();
for(Field field : fields){
System.out.println(field.getName());//age
}
//獲得類的所有屬性。包括私有的
Field [] allFields = c2.getDeclaredFields();
for(Field field : allFields){
System.out.println(field.getName());//name age
}
//獲得類的public類型的方法。這里包括 Object 類的一些方法
Method [] methods = c2.getMethods();
for(Method method : methods){
System.out.println(method.getName());//work waid equls toString hashCode等
}
//獲得類的所有方法。
Method [] allMethods = c2.getDeclaredMethods();
for(Method method : allMethods){
System.out.println(method.getName());//work say
}
//獲得指定的屬性
Field f1 = c2.getField("age");
System.out.println(f1);
//獲得指定的私有屬性
Field f2 = c2.getDeclaredField("name");
//啟用和禁用訪問安全檢查的開關,值為 true,則表示反射的對象在使用時應該取消 java 語言的訪問檢查;反之不取消
f2.setAccessible(true);
System.out.println(f2);
//創建這個類的一個對象
Object p2 = c2.newInstance();
//將 p2 對象的 f2 屬性賦值為 Bob,f2 屬性即為 私有屬性 name
f2.set(p2,"Bob");
//使用反射機制可以打破封裝性,導致了java對象的屬性不安全。
System.out.println(f2.get(p2)); //Bob
//獲取構造方法
Constructor [] constructors = c2.getConstructors();
for(Constructor constructor : constructors){
System.out.println(constructor.toString());//public com.ys.reflex.Person()
}
反射方法執行
public class Apple {
private int price;
public int getPrice() {
return price;
}
public void setPrice(int price) {
this.price = price;
}
public static void main(String[] args) throws Exception{
//正常的調用
Apple apple = new Apple();
apple.setPrice(5);
System.out.println("Apple Price:" + apple.getPrice());
//使用反射調用
Class clz = Class.forName("com.wyl.api.Apple");
Method setPriceMethod = clz.getMethod("setPrice", int.class);
Constructor appleConstructor = clz.getConstructor();
Object appleObj = appleConstructor.newInstance();
setPriceMethod.invoke(appleObj, 14);
Method getPriceMethod = clz.getMethod("getPrice");
System.out.println("Apple Price:" + getPriceMethod.invoke(appleObj));
}
}
