從net到java:java快速入門
學習java那是不可能的,到為什麼不學習一下呢。僅為總結。希望自己在不久的將來能書寫優美的java程式。加油!奧利給
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));
}
}