學習java那是不可能的癌蓖,到為什么不學習一下呢。僅為總結婚肆。希望自己在不久的將來能書寫優(yōu)美的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方法也是程序的主線程。
*/
文檔注釋:用于生產(chǎn)API文檔排惨,配合JavaDoc吭敢。
/*
* @Description HelloWorld類
* @Author 王延領
**/
1.2 idea注釋模版配置
1.2.1 定義java文件頭部的注釋
File => setting => editor => File and Code Templates-class -Includes
/**
* @創(chuàng)建人 王延領
*@創(chuàng)建時間 ${DATE}
*描述 Todo
**/
以上當你創(chuàng)建一個class的時候就會帶上以上信息了
1.2.2 給java類中的方法添加上注釋
第一步勾選Enable Live Templates
首先要在上一步中勾選中 Enable Live Templates
第二步新建一個Group
其次要打開LiveTemplates 然后新建一個Group
如圖:
在彈窗口中輸入你想要的group名稱,wyl
其中:Abbreviation 為快捷鍵,當輸入w的時候就會提示對應的方法注釋模板暮芭,j為類的注釋模板
Templete Text
注釋內(nèi)容鹿驼,$$ 為動態(tài)模板參數(shù)點擊Edit Vaariables 選擇對應動態(tài)值。
/*
* @描述: TODO
* @作者 王延領
* @時間 2021/7/12
* @版本 1.0
*/
public class wyl {
/**
*@描述
*@參數(shù) [str]
*@返回值 [java.lang.String]
*@創(chuàng)建人 王延領
*@創(chuàng)建時間 2021/7/12
*@修改人和其它信息
*/
public String CommentTemplate(String str)
{
return str;
}
}
2.關鍵字
關鍵字 | 說明 |
---|---|
private | 一種訪問控制方式:私用模式 |
protected | 一種訪問控制方式:保護模式 |
public | 一種訪問控制方式:共用模式 |
abstract | 表明類或者成員方法具有抽象屬性 |
class | 類 |
extends | 表明一個類型是另一個類型的子類型辕宏,這里常見的類型有類和接口 |
final | 用來說明最終屬性蠢沿,表明一個類不能派生出子類,或者成員方法不能被覆蓋匾效,或者成員域的值不能被改變 |
implements | 表明一個類實現(xiàn)了給定的接口 |
interface | 接口 |
native | 用來聲明一個方法是由與計算機相關的語言(如C/C++/FORTRAN語言)實現(xiàn)的 |
new | 用來創(chuàng)建新實例對象 |
static | 表明具有靜態(tài)屬性 |
strictfp | 用來聲明FP_strict(單精度或雙精度浮點數(shù))表達式遵循IEEE 754算術規(guī)范 |
synchronized | 表明一段代碼需要同步執(zhí)行 |
transient | 聲明不用序列化的成員域 |
volatile | 表明兩個或者多個變量必須同步地發(fā)生變化 |
break | 提前跳出一個塊 |
continue | 回到一個塊的開始處 |
return | 從成員方法中返回數(shù)據(jù) |
do | 用在do-while循環(huán)結構中 |
while | 用在循環(huán)結構中 |
if | 條件語句的引導詞 |
else | 用在條件語句中舷蟀,表明當條件不成立時的分支 |
for | 一種循環(huán)結構的引導詞 |
instanceof | 用來測試一個對象是否是指定類型的實例對象 |
switch | 分支語句結構的引導詞 |
case | 用在switch語句之中,表示其中的一個分支 |
default | 默認面哼,例如野宜,用在switch語句中,表明一個默認的分支 |
try | 嘗試一個可能拋出異常的程序塊 |
catch | 用在異常處理中魔策,用來捕捉異常 |
throw | 拋出一個異常 |
throws | 聲明在當前定義的成員方法中所有需要拋出的異常 |
import | 表明要訪問指定的類或包 |
package | 包 |
boolean | 基本數(shù)據(jù)類型之一匈子,布爾類型 |
byte | 基本數(shù)據(jù)類型之一,字節(jié)類型 |
char | 基本數(shù)據(jù)類型之一闯袒,字符類型 |
double | 基本數(shù)據(jù)類型之一虎敦,雙精度浮點數(shù)類型 |
float | 基本數(shù)據(jù)類型之一,單精度浮點數(shù)類型 |
int | 基本數(shù)據(jù)類型之一政敢,整數(shù)類型 |
long | 基本數(shù)據(jù)類型之一其徙,長整數(shù)類型 |
short | 基本數(shù)據(jù)類型之一,短整數(shù)類型 |
super | 表明當前對象的父類型的引用或者父類型的構造方法 |
this | 指向當前實例對象的引用 |
void | 聲明當前成員方法沒有返回值 |
goto | 保留關鍵字,沒有具體含義 |
const | 保留關鍵字喷户,沒有具體含義 |
3.數(shù)據(jù)類型
3.1.數(shù)據(jù)類型轉(zhuǎn)換
3.1.1自動類型轉(zhuǎn)換
自動類型轉(zhuǎn)換:容量小的數(shù)據(jù)類型可以自動轉(zhuǎn)換為容量大的數(shù)據(jù)類型唾那。
注:如果低級類型為char型,向高級類型(整型)轉(zhuǎn)換時褪尝,會轉(zhuǎn)換為對應ASCII碼值
3.1.2 強制類型轉(zhuǎn)換
強制類型轉(zhuǎn)換闹获,又被稱為造型,用于顯式的轉(zhuǎn)換一個數(shù)值的類型.
轉(zhuǎn)換方式為:(type)var 河哑,運算符“()”中的type表示將值var想要轉(zhuǎn)換成的目標數(shù)據(jù)類型避诽。 條件是轉(zhuǎn)換的數(shù)據(jù)類型必須是兼容的。
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.包裝類過渡類型轉(zhuǎn)換
- eg1:int i=Integer.parseInt(“123”)
說明:此方法只能適用于字符串轉(zhuǎn)化成整型變量 - eg2: float f=Float.valueOf(“123”).floatValue()
說明:上例是將一個字符串轉(zhuǎn)化成一個Float對象璃谨,然后再調(diào)用這個對象的floatValue()方法返回其對應的float數(shù)值沙庐。 - eg3: boolean b=Boolean.valueOf(“123”).booleanValue()
說明:上例是將一個字符串轉(zhuǎn)化成一個Boolean對象,然后再調(diào)用這個對象的booleanValue()方法返回其對應的boolean數(shù)值。 - eg4:double d=Double.valueOf(“123”).doublue()
說明:上例是將一個字符串轉(zhuǎn)化成一個Double對象轨功,然后再調(diào)用這個對象的doublue()方法返回其對應的double數(shù)值旭斥。 - eg5: long l=Long.valueOf(“123”).longValue()
說明:上例是將一個字符串轉(zhuǎn)化成一個Long對象,然后再調(diào)用這個對象的longValue()方法返回其對應的long數(shù)值古涧。 - eg6: char=Character.valueOf(“123”).charValue()
說明:上例是將一個字符串轉(zhuǎn)化成一個Character對象
++++++++++++++++++++++++++++++++++++++++++++++++++++++
4.常量垂券、變量、運算符
常量
變量是什么:就是可以變化的量羡滑!
我們通過變量來操縱存儲空間中的數(shù)據(jù)菇爪,變量就是指代這個存儲空間!空間位置是確定的柒昏,但是里面放
置什么值不確定凳宙!Java是一種強類型語言,每個變量都必須聲明其類型职祷。
//數(shù)據(jù)類型 變量名 = 值氏涩;可以使用逗號隔開來聲明多個同類型變量。
注意事項:
每個變量都有類型有梆,類型可以是基本類型是尖,也可以是引用類型。
變量名必須是合法的標識符泥耀。
變量聲明是一條完整的語句饺汹,因此每一個聲明都必須以分號結束
變量作用域
類變量(靜態(tài)變量: static variable):獨立于方法之外的變量,用 static 修飾痰催。
實例變量(成員變量:member variable):獨立于方法之外的變量兜辞,不過沒有 static 修飾。
局部變量(lacal variable):類的方法中的變量夸溶。
變量
常量(Constant):初始化(initialize)后不能再改變值逸吵!不會變動的值。
final 常量名=值;
final double PI=3.14;
命名規(guī)范
- 所有變量蜘醋、方法胁塞、類名:見名知意
- 類成員變量:首字母小寫和駝峰原則 : monthSalary
- 局部變量:首字母小寫和駝峰原則
- 常量:大寫字母和下劃線:MAX_VALUE
- 類名:首字母大寫和駝峰原則: Man, GoodMan
- 方法名:首字母小寫和駝峰原則: run(), runRun()
運算符
Java 語言支持如下運算符:
算術運算符: +,-压语,,/编检,%胎食,++,--
賦值運算符 =
關系運算符: >允懂,<厕怜,>=,<=,==粥航,!= instanceof
邏輯運算符: &&琅捏,||,!
位運算符: &递雀,|柄延,^,~ 缀程, >>搜吧,<<,>>> (了解Q畲铡B四巍!)
條件運算符 撩满?:
擴展賦值運算符:+=蜒程,-=,=伺帘,/=
5.java流轉(zhuǎn)控制
if...else昭躺、while、do...while曼追、for窍仰、switch...case 在這就不累述了。
跳轉(zhuǎn):
return
return從一個方法返回礼殊,并把控制權交給調(diào)用它的語句序驹吮;或者直接結束當前的程序;
break
break語句在for晶伦、while碟狞、do···while循環(huán)語句中,經(jīng)常用于強行退出當前循環(huán)婚陪;
continue
continue語句用于跳過此次循環(huán)族沃,執(zhí)行下次循環(huán);
6.方法
那么什么是方法呢泌参?
Java方法是語句的集合脆淹,它們在一起執(zhí)行一個功能。
方法是解決一類問題的步驟的有序組合
方法包含于類或?qū)ο笾?br>
方法在程序中被創(chuàng)建沽一,在其他地方被引用
設計方法的原則:方法的本意是功能塊盖溺,就是實現(xiàn)某個功能的語句塊的集合。我們設計方法的時候铣缠,最
好保持方法的原子性烘嘱,就是一個方法只完成1個功能昆禽,這樣利于我們后期的擴展。
方法的優(yōu)點
使程序變得更簡短而清晰蝇庭。
有利于程序維護醉鳖。
可以提高程序開發(fā)的效率。
提高了代碼的重用性哮内。
定義
修飾符 返回值類型 方法名(參數(shù)類型 參數(shù)名){
...
方法體
...c
return 返回值;
}
修飾符:修飾符盗棵,這是可選的,告訴編譯器如何調(diào)用該方法牍蜂。定義了該方法的訪問類型漾根。
返回值類型 :方法可能會返回值。returnValueType 是方法返回值的數(shù)據(jù)類型鲫竞。有些方法執(zhí)行所需
的操作辐怕,但沒有返回值。在這種情況下从绘,returnValueType 是關鍵字void寄疏。
方法名:是方法的實際名稱愉老。方法名和參數(shù)表共同構成方法簽名联喘。
參數(shù)類型:參數(shù)像是一個占位符。當方法被調(diào)用時逗载,傳遞值給參數(shù)批什。這個值被稱為實參或變量农曲。參
數(shù)列表是指方法的參數(shù)類型、順序和參數(shù)的個數(shù)驻债。參數(shù)是可選的乳规,方法可以不包含任何參數(shù)。
形式參數(shù):在方法被調(diào)用時用于接收外界輸入的數(shù)據(jù)合呐。
實參:調(diào)用方法時實際傳給方法的數(shù)據(jù)暮的。
方法體:方法體包含具體的語句,定義該方法的功能淌实。
方法的重載
就是說一個類的兩個方法擁有相同的名字冻辩,但是有不同的參數(shù)列表。
可變參數(shù)
在方法聲明中拆祈,在指定參數(shù)類型后加一個省略號(...) 恨闪。
一個方法中只能指定一個可變參數(shù),它必須是方法的最后一個參數(shù)放坏。任何普通的參數(shù)必須在它之前聲
明凛剥。
typeName... parameterName
遞歸
自己調(diào)用自己
7.數(shù)組
數(shù)組的定義:
數(shù)組是相同類型數(shù)據(jù)的有序集合.
數(shù)組描述的是相同類型的若干個數(shù)據(jù),按照一定的先后次序排列組合而成。
其中,每一個數(shù)據(jù)稱作一個數(shù)組元素,每個數(shù)組元素可以通過一個下標來訪問它們.
數(shù)組的四個基本特點:
- 其長度是確定的轻姿。數(shù)組一旦被創(chuàng)建犁珠,它的大小就是不可以改變的。
- 其元素必須是相同類型,不允許出現(xiàn)混合類型互亮。
- 數(shù)組中的元素可以是任何數(shù)據(jù)類型犁享,包括基本類型和引用類型。
- 數(shù)組變量屬引用類型豹休,數(shù)組也可以看成是對象炊昆,數(shù)組中的每個元素相當于該對象的成員變量
數(shù)組聲明
dataType[] arrayRefVar; // 首選的方法
或
dataType arrayRefVar[]; // 效果相同,但不是首選方法
創(chuàng)建數(shù)組
arrayRefVar = new dataType[1 arraySize];
數(shù)組的元素是通過索引訪問的威根。數(shù)組索引從 0 開始凤巨,所以索引值從 0 到 arrayRefVar.length-1。
三種初始化
靜態(tài)初始化
除了用new關鍵字來產(chǎn)生數(shù)組以外,還可以直接在定義數(shù)組的同時就為數(shù)組元素分配空間并賦值洛搀。
int[] a = {1,2,3};
Man[] mans = {new Man(1,1),new Man(2,2)};
動態(tài)初始化
數(shù)組定義敢茁、為數(shù)組元素分配空間、賦值的操作留美、分開進行彰檬。
int[] a = new int[2];
a[0]=1;
a[1]=2;
數(shù)組的默認初始化
數(shù)組是引用類型,它的元素相當于類的實例變量谎砾,因此數(shù)組一經(jīng)分配空間逢倍,其中的每個元素也被按照實
例變量同樣的方式被隱式初始化。
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
}
數(shù)組邊界
下標的合法區(qū)間:[0, length-1]景图,如果越界就會報錯较雕;
for 和For-Each 循環(huán)
for(type element: array){
System.out.println(element);
}
for (int i = 1; i < myList.length; i++) {
System.out.println(myList[i]);
}
多維數(shù)組
type[][] typeName = new type[typeLength1][1 typeLength2];
Arrays 類
數(shù)組的工具類java.util.Arrays
java.util.Arrays 類能方便地操作數(shù)組. 使用之前需要導包!
具有以下常用功能:
給數(shù)組賦值:通過 fill 方法挚币。
對數(shù)組排序:通過 sort 方法,按升序亮蒋。
比較數(shù)組:通過 equals 方法比較數(shù)組中元素值是否相等。
查找數(shù)組元素:通過 binarySearch 方法能對排序好的數(shù)組進行二分查找法操作忘晤。
轉(zhuǎn)換為list: 通過asList(a)進行轉(zhuǎn)換
8.面向?qū)ο?/h1>
萬物皆為對象M痱尽!设塔!對象是抽象概念的具體實例凄吏。
以類的方式組織代碼,以對象的組織(封裝)數(shù)據(jù)就是面向?qū)ο?/p>
繼承
繼承是java面向?qū)ο缶幊碳夹g的一塊基石闰蛔,因為它允許創(chuàng)建分等級層次的類痕钢。
class 父類 {
}
class 子類 extends 父類 {
}
public interface A {
public void eat();
public void sleep();
}
public interface B {
public void show();
}
public class C implements A,B {
}
為什么要繼承,因為有重復序六。所以才繼承任连,進而我們就知道了。父類就是公共部分的定義或規(guī)則
Java 不支持多繼承(只能繼承一個類)例诀,但支持多重繼承随抠。
特點
子類擁有父類非 private 的屬性裁着、方法。
子類可以擁有自己的屬性和方法拱她,即子類可以對父類進行擴展二驰。
子類可以用自己的方式實現(xiàn)父類的方法。
Java 的繼承是單繼承秉沼,但是可以多重繼承桶雀,單繼承就是一個子類只能繼承一個父類,多重繼承就是唬复,例如 B 類繼承 A 類矗积,C 類繼承 B 類,所以按照關系就是 B 類是 C 類的父類敞咧,A 類是 B 類的父類棘捣,這是 Java 繼承區(qū)別于 C++ 繼承的一個特性。
提高了類之間的耦合性(繼承的缺點妄均,耦合度高就會造成代碼之間的聯(lián)系越緊密柱锹,代碼獨立性越差)
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 調(diào)用自己的方法
super.eat(); // super 調(diào)用父類方法
}
}
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(){ // 自動調(diào)用父類的無參數(shù)構造器
System.out.println("SubClass");
}
public SubClass(int n){
super(300); // 調(diào)用父類中帶有參數(shù)的構造器
System.out.println("SubClass(int n):"+n);
this.n = n;
}
}
// SubClass2 類繼承
class SubClass2 extends SuperClass{
private int n;
SubClass2(){
super(300); // 調(diào)用父類中帶有參數(shù)的構造器
System.out.println("SubClass2");
}
public SubClass2(int n){ // 自動調(diào)用父類的無參數(shù)構造器
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
構造函數(shù)
子類是不繼承父類的構造器(構造方法或者構造函數(shù))的禁熏,它只是調(diào)用(隱式或顯式)。如果父類的構造器帶有參數(shù)邑彪,則必須在子類的構造器中顯式地通過 super 關鍵字調(diào)用父類的構造器并配以適當?shù)膮?shù)列表瞧毙。
如果父類構造器沒有參數(shù),則在子類的構造器中不需要使用 super 關鍵字調(diào)用父類構造器寄症,系統(tǒng)會自動調(diào)用父類的無參構造器宙彪。
重寫(Override)與重載(Overload)
重寫(Override)
重寫是子類對父類的允許訪問的方法的實現(xiàn)過程進行重新編寫, 返回值和形參都不能改變。即外殼不變有巧,核心重寫
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();// 執(zhí)行 Animal 類的方法
b.move();//執(zhí)行 Dog 類的方法
}
}
動物可以移動
狗可以跑和走
方法的重寫規(guī)則
參數(shù)列表與被重寫方法的參數(shù)列表必須完全相同释漆。
返回類型與被重寫方法的返回類型可以不相同,但是必須是父類返回值的派生類(java5 及更早版本返回類型要一樣篮迎,java7 及更高版本可以不同)男图。
訪問權限不能比父類中被重寫的方法的訪問權限更低。例如:如果父類的一個方法被聲明為 public甜橱,那么在子類中重寫該方法就不能聲明為 protected逊笆。
父類的成員方法只能被它的子類重寫。
聲明為 final 的方法不能被重寫岂傲。
聲明為 static 的方法不能被重寫难裆,但是能夠被再次聲明。
子類和父類在同一個包中,那么子類可以重寫父類所有方法乃戈,除了聲明為 private 和 final 的方法褂痰。
子類和父類不在同一個包中,那么子類只能夠重寫父類的聲明為 public 和 protected 的非 final 方法偏化。
重寫的方法能夠拋出任何非強制異常脐恩,無論被重寫的方法是否拋出異常。但是侦讨,重寫的方法不能拋出新的強制性異常,或者比被重寫方法聲明的更廣泛的強制性異常苟翻,反之則可以韵卤。
構造方法不能被重寫。
如果不能繼承一個類崇猫,則不能重寫該類的方法沈条。
重載(Overload)
重載(overloading) 是在一個類里面,方法名字相同诅炉,而參數(shù)不同蜡歹。返回類型可以相同也可以不同。
每個重載的方法(或者構造函數(shù))都必須有一個獨一無二的參數(shù)類型列表涕烧。
最常用的地方就是構造器的重載月而。
public class Overloading {
public int test(){
System.out.println("test1");
return 1;
}
public void test(int a){
System.out.println("test2");
}
//以下兩個參數(shù)類型順序不同
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));
}
}
重載規(guī)則:
被重載的方法必須改變參數(shù)列表(參數(shù)個數(shù)或類型不一樣);
被重載的方法可以改變返回類型议纯;
被重載的方法可以改變訪問修飾符父款;
被重載的方法可以聲明新的或更廣的檢查異常;
方法能夠在同一個類中或者在一個子類中被重載瞻凤。
-
無法以返回值類型作為重載函數(shù)的區(qū)分標準憨攒。
區(qū)別點 重載方法 重寫方法 參數(shù)列表 必須修改 一定不能修改 返回類型 可以修改 一定不能修改 異常 可以修改 可以減少或刪除,一定不能拋出新的或者更廣的異常 訪問 可以修改 一定不能做更嚴格的限制(可以降低限制)
多態(tài)
多態(tài)是同一個行為具有多個不同表現(xiàn)形式或形態(tài)的能力阀参。
多態(tài)就是同一個接口肝集,使用不同的實例而執(zhí)行不同操作。
多態(tài)性是對象多種表現(xiàn)形式的體現(xiàn)蛛壳。
多態(tài)的優(yōu)點
- 消除類型之間的耦合關系
- 可替換性
- 可擴充性
- 接口性
- 靈活性
- 簡化性
多態(tài)存在的三個必要條件
繼承
重寫
父類引用指向子類對象: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()");
}
}
虛函數(shù)
虛函數(shù)的存在是為了多態(tài)杏瞻。
Java 中其實沒有虛函數(shù)的概念,它的普通函數(shù)就相當于 C++ 的虛函數(shù)炕吸,動態(tài)綁定是Java的默認行為伐憾。如果 Java 中不希望某個函數(shù)具有虛函數(shù)特性,可以加上 final 關鍵字變成非虛函數(shù)赫模。
多態(tài)的實現(xiàn)方式
方式一:重寫:
方式二:接口
方式三:抽象類和抽象方法
抽象類
擁有抽象方法的類就是抽象類树肃,抽象類要使用abstract關鍵字聲明.
abstract class A{//定義一個抽象類
public void fun(){//普通方法
System.out.println("存在方法體的方法");
}
public abstract void print();//抽象方法,沒有方法體瀑罗,有abstract關鍵字做修飾
}
繼承抽象類
我們可以通過以下方式繼承 Employee 類的屬性
抽象類的使用原則
(1)抽象方法必須為public或者protected(因為如果為private胸嘴,則不能被子類繼承雏掠,子類便無法實現(xiàn)該方法),缺省情況下默認為public劣像;
(2)抽象類不能直接實例化乡话,需要依靠子類采用向上轉(zhuǎn)型的方式處理;
(3)抽象類必須有子類耳奕,使用extends繼承绑青,一個子類只能繼承一個抽象類;
(4)子類(如果不是抽象類)則必須覆寫抽象類之中的全部抽象方法(如果子類沒有實現(xiàn)父類的抽象方法屋群,則必須將子類也定義為為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();//向上轉(zhuǎn)型
a.print();//被子類所覆寫的過的方法
}
}
Hello World !
封裝
封裝(英語:Encapsulation)是指一種將抽象性函式接口的實現(xiàn)細節(jié)部分包裝对竣、隱藏起來的方法庇楞。
封裝可以被認為是一個保護屏障,防止該類的代碼和數(shù)據(jù)被外部類定義的代碼隨機訪問否纬。
要訪問該類的代碼和數(shù)據(jù)吕晌,必須通過嚴格的接口控制。
封裝最主要的功能在于我們能修改自己的實現(xiàn)代碼烦味,而不用修改那些調(diào)用我們代碼的程序片段聂使。
適當?shù)姆庋b可以讓程式碼更容易理解與維護,也加強了程式碼的安全性谬俄。
封裝的優(yōu)點
- 良好的封裝能夠減少耦合柏靶。
- 類內(nèi)部的結構可以自由修改。
- 可以對成員變量進行更精確的控制溃论。
- 隱藏信息屎蜓,實現(xiàn)細節(jié)。
接口
在JAVA編程語言中是一個抽象類型钥勋,是抽象方法的集合炬转,接口通常以interface來聲明。一個類通過繼承接口的方式算灸,從而來繼承接口的抽象方法扼劈。
接口與類相似點
- 一個接口可以有多個方法。
- 接口文件保存在 .java 結尾的文件中菲驴,文件名使用接口名荐吵。
- 接口的字節(jié)碼文件保存在 .class 結尾的文件中。
- 接口相應的字節(jié)碼文件必須在與包名稱相匹配的目錄結構中。
接口與類的區(qū)別
- 接口不能用于實例化對象先煎。
- 接口沒有構造方法贼涩。
- 接口中所有的方法必須是抽象方法,Java 8 之后 接口中可以使用 default 關鍵字修飾的非抽象方法薯蝎。
- 接口不能包含成員變量遥倦,除了 static 和 final 變量。
- 接口不是被類繼承了占锯,而是要被類實現(xiàn)袒哥。
- 接口支持多繼承。
接口特性
- 接口中每一個方法也是隱式抽象的,接口中的方法會被隱式的指定為 public abstract(只能是 public abstract烟央,其他修飾符都會報錯)统诺。
- 接口中可以含有變量,但是接口中的變量會被隱式的指定為 public static final 變量(并且只能是 public疑俭,用 private 修飾會報編譯錯誤)。
- 接口中的方法是不能在接口中實現(xiàn)的婿失,只能由實現(xiàn)接口的類來實現(xiàn)接口中的方法钞艇。
抽象類和接口的區(qū)別
抽象類中的方法可以有方法體,就是能實現(xiàn)方法的具體功能豪硅,但是接口中的方法不行哩照。
抽象類中的成員變量可以是各種類型的,而接口中的成員變量只能是 public static final 類型的懒浮。
接口中不能含有靜態(tài)代碼塊以及靜態(tài)方法(用 static 修飾的方法)飘弧,而抽象類是可以有靜態(tài)代碼塊和靜態(tài)方法
一個類只能繼承一個抽象類,而一個類卻可以實現(xiàn)多個接口砚著。
[可見度] interface 接口名稱 [extends 其他的接口名] {
// 聲明變量
// 抽象方法
}
/* 文件名 : NameOfInterface.java */
import java.lang.*;
//引入包
public interface NameOfInterface
{
//任何類型 final, static 字段
//抽象方法
}
接口有以下特性
- 接口是隱式抽象的次伶,當聲明一個接口的時候,不必使用abstract關鍵字稽穆。
- 接口中每一個方法也是隱式抽象的冠王,聲明時同樣不需要abstract關鍵字。
- 接口中的方法都是公有的舌镶。
枚舉
枚舉是一個特殊的類柱彻,一般表示一組常量.每個枚舉都是通過 Class 在內(nèi)部實現(xiàn)的,且所有的枚舉值都是 public static final 的餐胀。
enum Color
{
RED, GREEN, BLUE;
}
public class Test
{
// 執(zhí)行輸出結果
public static void main(String[] args)
{
Color c1 = Color.RED;
System.out.println(c1);
}
}
RED
values(), ordinal() 和 valueOf() 方法
enum 定義的枚舉類默認繼承了 java.lang.Enum 類哟楷,并實現(xiàn)了 java.lang.Seriablizable 和 java.lang.Comparable 兩個接口。
values(), ordinal() 和 valueOf() 方法位于 java.lang.Enum 類中:
values() 返回枚舉類中所有的值否灾。
ordinal()方法可以找到每個枚舉常量的索引卖擅,就像數(shù)組索引一樣。
-
valueOf()方法返回指定字符串值的枚舉常量。
enum Color { RED, GREEN, BLUE; } public class Test { public static void main(String[] args) { // 調(diào)用 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
枚舉成員
枚舉跟普通類一樣可以用自己的變量、方法和構造函數(shù)屏歹,構造函數(shù)只能使用 private 訪問修飾符票腰,所以外部無法調(diào)用。
enum Color { RED, GREEN, BLUE; // 構造函數(shù) private Color() { System.out.println("Constructor called for : " + this.toString()); } public void colorInfo() { System.out.println("Universal Color"); } }
包(package)
為了更好地組織類恰梢,Java 提供了包機制,用于區(qū)別類名的命名空間。
包的 3 個作用如下
區(qū)分相同名稱的類统刮。
能夠較好地管理大量的類。
控制訪問范圍账千。
定義
```java
package 包名;
Java 包的命名規(guī)則如下:
* 包名全部由小寫字母(多個單詞也全部小寫)侥蒙。
* 如果包名包含多個層次,每個層次用“.”分割匀奏。
* 包名一般由倒置的域名開頭鞭衩,比如 com.baidu,不要有 www娃善。
* 自定義包不能 java 開頭
**包導入**
\如果使用不同包中的其它類论衍,需要使用該類的全名(包名+類名)
example.Test test = new example.Test();
```c
\\import 包名+類名;
import example.Test;\\or
import example.*;
系統(tǒng)包
包 | 說明 |
---|---|
java.lang | Java 的核心類庫,包含運行 Java 程序必不可少的系統(tǒng)類聚磺,如基本數(shù)據(jù)類型坯台、基本數(shù)學函數(shù)、 字符串處理瘫寝、異常處理和線程類等蜒蕾,系統(tǒng)默認加載這個包 |
java.io | Java 語言的標準輸入/輸出類庫,如基本輸入/輸出流焕阿、文件輸入/輸出咪啡、過濾輸入/輸出流等 |
java.util | 包含如處理時間的 Date 類,處理動態(tài)數(shù)組的 Vector 類捣鲸,以及 Stack 和 HashTable 類 |
java.awt | 構建圖形用戶界面(GUI)的類庫瑟匆,低級繪圖操作 Graphics 類、圖形界面組件和布局管理 (如 Checkbox 類栽惶、Container 類愁溜、LayoutManger 接口等),以及用戶界面交互控制和事 件響應(如 Event 類) |
java.awt.image | 處理和操縱來自網(wǎng)上的圖片的 Java 工具類庫 |
java.wat.peer | 很少在程序中直接用到外厂,使得同一個 Java 程序在不同的軟硬件平臺上運行 |
java.net | 實現(xiàn)網(wǎng)絡功能的類庫有 Socket 類冕象、ServerSocket 類 |
java.lang.reflect | 提供用于反射對象的工具 |
java.util.zip | 實現(xiàn)文件壓縮功能 |
java.awt.datatransfer | 處理數(shù)據(jù)傳輸?shù)墓ぞ哳悾糍N板汁蝶、字符串發(fā)送器等 |
java.sql | 實現(xiàn) JDBC 的類庫 |
java.rmi | 提供遠程連接與載入的支持 |
java. security | 提供安全性方面的有關支持 |
9.異常處理
異常處理的概念
是編程語言或計算機硬件里的一種機制渐扮,用于處理軟件或信息系統(tǒng)中出現(xiàn)的異常狀況(即超出程序正常執(zhí)行流程的某些特殊條件)论悴。
關鍵字
Java異常機制用到的幾個關鍵字:try、catch墓律、finally膀估、throw、throws耻讽。
try -- 用于監(jiān)聽察纯。將要被監(jiān)聽的代碼(可能拋出異常的代碼)放在try語句塊之內(nèi),當try語句塊內(nèi)發(fā)生異常
時针肥,異常就被拋出饼记。
catch -- 用于捕獲異常。catch用來捕獲try語句塊中發(fā)生的異常慰枕。
finally -- finally語句塊總是會被執(zhí)行具则。它主要用于回收在try塊里打開的物力資源(如數(shù)據(jù)庫連接、網(wǎng)絡
連接和磁盤文件)具帮。只有finally塊博肋,執(zhí)行完成之后,才會回來執(zhí)行try或者catch塊中的return或者throw語
句蜂厅,如果finally中使用了return或者throw等終止方法的語句束昵,則就不會跳回執(zhí)行,直接停止葛峻。
throw -- 用于拋出異常。
throws -- 用在方法簽名中巴比,用于聲明該方法可能拋出的異常术奖。
try{
可能會發(fā)生的異常
}catch(異常類型 異常名(變量)){
針對異常進行處理的代碼
}catch(異常類型 異常名(變量)){
針對異常進行處理的代碼
}...
[finally{
釋放資源代碼;
}]
Error與Exception區(qū)別
Error(錯誤)是系統(tǒng)中的錯誤轻绞,程序員是不能改變的和處理的采记,是在程序編譯時出現(xiàn)的錯誤,只能通過修改程序才能修正政勃。一般是指與虛擬機相關的問題唧龄,如系統(tǒng)崩潰,虛擬機錯誤奸远,內(nèi)存空間不足既棺,方法調(diào)用棧溢等。對于這類錯誤的導致的應用程序中斷懒叛,僅靠程序本身無法恢復和和預防丸冕,遇到這樣的錯誤,建議讓程序終止薛窥。
Exception(異常)表示程序可以處理的異常胖烛,可以捕獲且可能恢復眼姐。遇到這類異常,應該盡可能處理異常佩番,使程序恢復運行众旗,而不應該隨意終止異常。
throw與throws區(qū)別
throw:指的是在方法中人為拋出一個異常對象(這個異常對象可能是自己實例化或者拋出已存在的)趟畏;
throw ThrowableInstance;
throws:在方法的聲明上使用贡歧,表示此方法在調(diào)用時必須處理異常。
throw new NullPointerException("demo");
Java異常層次結構圖(網(wǎng)上獲裙案洹)
10.集合框架
所有集合類都位于 java.util 包下艘款。Java的集合類主要由兩個接口派生而出:Collection 和 Map,Collection 和 Map 是 Java 集合框架的根接口沃琅,這兩個接口又包含了一些子接口或?qū)崿F(xiàn)類哗咆。
集合框架被設計成要滿足以下幾個目標:
- 該框架必須是高性能的∫婷迹基本集合(動態(tài)數(shù)組晌柬,鏈表,樹郭脂,哈希表)的實現(xiàn)也必須是高效的年碘。
- 該框架允許不同類型的集合,以類似的方式工作展鸡,具有高度的互操作性屿衅。
- 對一個集合的擴展和適應必須是簡單的。
集合框架都包含如下內(nèi)容:
接口:是代表集合的抽象數(shù)據(jù)類型莹弊。例如 Collection涤久、List、Set忍弛、Map 等响迂。之所以定義多個接口,是為了以不同的方式操作集合對象
實現(xiàn)(類):是集合接口的具體實現(xiàn)细疚。從本質(zhì)上講蔗彤,它們是可重復使用的數(shù)據(jù)結構,例如:ArrayList疯兼、LinkedList然遏、HashSet、HashMap镇防。
-
算法:是實現(xiàn)集合接口的對象里的方法執(zhí)行的一些有用的計算啦鸣,例如:搜索和排序。這些算法被稱為多態(tài)来氧,那是因為相同的方法可以在相似的接口上有著不同的實現(xiàn)诫给。
Collection是一個基本的集合接口香拉,Collection中可以容納一組集合元素(Element)
Collection 接口
Collection 是最基本的集合接口,一個 Collection 代表一組 Object中狂,即 Collection 的元素, Java不提供直接繼承自Collection的類凫碌,只提供繼承于的子接口(如List和set)。
List
List接口是一個有序, 元素可重復的 Collection胃榕,使用此接口能夠精確的控制每個元素插入的位置盛险,能夠通過索引(元素在List中位置,類似于數(shù)組的下標)來訪問List中的元素勋又,第一個元素的索引為 0苦掘,而且允許有相同的元素。
- ArrayList
底層數(shù)據(jù)結構是數(shù)組楔壤,查改快鹤啡,增刪慢。
非線程安全蹲嚣,效率高
方法:
排序
import java.util.Collections; // 引入 Collections 類
Collections.sort(sites); *// 字母排序*
- Vector
底層數(shù)據(jù)結構是數(shù)組递瑰,查改快,增刪慢隙畜。
線程安全抖部,效率低
- LinkedList
底層數(shù)據(jù)結構是鏈表,查改慢议惰,增刪快慎颗。
非線程安全,效率高
以下情況使用 LinkedList :
- 你需要通過循環(huán)迭代來訪問列表中的某些元素言询。
- 需要頻繁的在列表開頭哗总、中間、末尾等位置進行添加和刪除元素操作倍试。
LinkedList 繼承了 AbstractSequentialList 類。
LinkedList 實現(xiàn)了 Queue 接口蛋哭,可作為隊列使用县习。
LinkedList 實現(xiàn)了 List 接口,可進行列表的相關操作谆趾。
LinkedList 實現(xiàn)了 Deque 接口躁愿,可作為隊列使用。
LinkedList 實現(xiàn)了 Cloneable 接口沪蓬,可實現(xiàn)克隆彤钟。
LinkedList 實現(xiàn)了 java.io.Serializable 接口,即可支持序列化跷叉,能通過序列化去傳輸逸雹。
方法:
Set
Set 接口存儲一組唯一营搅,無序的對象。
- HashSet
底層數(shù)據(jù)結構是哈希表梆砸。(無序,唯一)
依賴兩個方法: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
底層數(shù)據(jù)結構是鏈表和哈希表。(FIFO插入有序,唯一)
1.由鏈表保證元素有序
2.由哈希表保證元素唯一
- TreeSet
底層數(shù)據(jù)結構是紅黑樹帖世。(唯一休蟹,有序)
如何保證元素排序的呢? 自然排序,比較器排序
Set和List的區(qū)別
- Set 接口實例存儲的是無序的日矫,不重復的數(shù)據(jù)赂弓。List 接口實例存儲的是有序的,可以重復的元素哪轿。
- Set檢索效率低下盈魁,刪除和插入效率高,插入和刪除不會引起元素位置改變 <實現(xiàn)類有HashSet,TreeSet>缔逛。
- List和數(shù)組類似备埃,可以動態(tài)增長,根據(jù)實際存儲的數(shù)據(jù)的長度自動增長List的長度褐奴。查找元素效率高按脚,插入刪除效率低,因為會引起其他元素位置改變 <實現(xiàn)類有ArrayList,LinkedList,Vector> 敦冬。
Map與Collection是并列關系辅搬。Map提供鍵(key)到值(value)的映射。一個Map中不能包含相同的鍵脖旱,每個鍵只能映射一個值堪遂。
- HashMap
無序,非線程安全萌庆,效率高溶褪。HashMap允許null值(key和value都允許)。
- HashTable
無序践险,線程安全猿妈,效率低。除構造函數(shù)外巍虫,HashTable的所有 public 方法聲明中都有 synchronized關鍵字彭则,而HashMap的源碼中則沒有。HashTable不允許null值(key和value都允許)占遥。
- TreeMap
有序俯抖,非線程安全,效率高(O(logN))瓦胎,但比不上HashMap (O(1))芬萍。
11.流(Stream)尤揣、文件(File)和IO
Java.io 包中定義了多個流類型(類或抽象類)來實現(xiàn)輸入/輸出功能;
可以從不同的角度對其進行分
類:
1.按數(shù)據(jù)流的方向不同可以分為輸入流【InputStream(字節(jié)流)担忧,Reader(字符流)】和輸出流【OutPutStream(字節(jié)流)芹缔,Writer(字符流)】
2.按照處理數(shù)據(jù)單位不同可以分為字節(jié)流【一個字節(jié)(Byte)是8位(bit))】和字符流【一個字符是2個字節(jié)】
3.按照功能不同可以分為節(jié)點流和處理流
[圖片上傳失敗...(image-da6cbd-1628583810189)]
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");
}
}
}
上面的程序首先創(chuàng)建文件test.txt,并把給定的數(shù)字以二進制形式寫進該文件瓶盛,同時輸出到控制臺上最欠。
以上代碼由于是二進制寫入,可能存在亂碼惩猫,你可以使用以下代碼實例來解決亂碼問題:
//文件名 :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對象,參數(shù)可以指定編碼,默認為操作系統(tǒng)默認編碼,windows上是gbk
writer.append("中文輸入");
// 寫入到緩沖區(qū)
writer.append("\r\n");
// 換行
writer.append("English");
// 刷新緩存沖,寫入到文件,如果下面已經(jīng)沒有寫入的內(nèi)容了,直接close也會寫入
writer.close();
// 關閉寫入流,同時會把緩沖區(qū)內(nèi)容寫入文件,所以上面的注釋掉
fop.close();
// 關閉輸出流,釋放系統(tǒng)資源
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());
// 轉(zhuǎn)成char加到StringBuffer對象中
}
System.out.println(sb.toString());
reader.close();
// 關閉讀取流
fip.close();
// 關閉輸入流,釋放系統(tǒng)資源
}
}
Reader 流與Writer流
Reader ,Write與InputStream 芝硬,OutputStream: 唯一的區(qū)別就在于讀的數(shù)據(jù)單位不同分別為(16bit),(8bit)
創(chuàng)建讀取目錄:
import java.io.File;
public class CreateDir {
public static void main(String[] args) {
String dirname = "/tmp/user/java/bin";
File d = new File(dirname);
// 現(xiàn)在創(chuàng)建目錄
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();
}
}
緩存流
是處理流的一種轧房,它是要“套接”在相應的節(jié)點流之上拌阴,對讀寫的數(shù)據(jù)提供了緩沖的功能,避免頻繁讀寫硬盤奶镶, 提高了讀寫的效率迟赃。同時增加了一些新的方法。
BufferedReader(Reader in)
BufferedReader(Reader in,int sz) //sz 為自定義緩沖區(qū)的大小
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節(jié)點流的外面套接一層處理流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"));
//在節(jié)點流FileWriter的外面再套一層處理流BufferedWriter
String s = null;
for(int i=0;i<100;i++){
s = String.valueOf(Math.random());//“Math.random()”將會生成一系列介于0~1之間的隨機數(shù)厂镇。
// static String valueOf(double d)這個valueOf()方法的作用就是把
一個double類型的數(shù)轉(zhuǎn)換成字符串
//valueOf()是一個靜態(tài)方法纤壁,所以可以使用“類型.靜態(tài)方法名”的形式來調(diào)用
bw.write(s);//把隨機數(shù)字符串寫入到指定文件中
bw.newLine();//調(diào)用newLine()方法使得每寫入一個隨機數(shù)就換行顯示
}
bw.flush();//調(diào)用flush()方法清空緩沖區(qū)
BufferedReader br = new BufferedReader(new FileReader("a:\\Student.txt"));
//在節(jié)點流FileReader的外面再套一層處理流BufferedReader
while((s = br.readLine())!=null){
//使用BufferedReader處理流里面提供String readLine()方法讀取文件中的數(shù)據(jù)時是一行一行讀取的
//循環(huán)結束的條件就是使用readLine()方法讀取數(shù)據(jù)返回的字符串為空值后則表
示已經(jīng)讀取到文件的末尾了。
System.out.println(s);
}
bw.close();
br.close();
}catch(Exception e){
e.printStackTrace();
}
}
}
轉(zhuǎn)換流
InputStreamReader 和 OutputStreamWriter 用于字節(jié)數(shù)據(jù)到字符數(shù)據(jù)之間的轉(zhuǎn)換
InputStreamReader 需要和 InputStream “套接” 捺信。
OutputStreamWriter 需要和 OutputStream “套接” 酌媒。
轉(zhuǎn)換流在構造時可以指定其編碼集合
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()方法取得當前系統(tǒng)的默認字符編碼
osw.close();
osw = new OutputStreamWriter(new FileOutputStream("D:\\java\\char.txt", true), "utf-8");// 如果在調(diào)用FileOutputStream的構造方法時沒有加入true,那么新加入的字符 串就會替換掉原來寫入的字符串迄靠,在調(diào)用構造方法時指定了字符的編碼
osw.write("不想搭理她");// 再次向指定的文件寫入字符串秒咨,新寫入的字符串加入到原來字符串的后面
System.out.println(osw.getEncoding());
osw.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
數(shù)據(jù)流
數(shù)據(jù)流 DataInputStream DataOutputStream 【分別繼承自InputStream 和 OutputStream】等-提供將基礎數(shù)據(jù)類型寫入到文件中,或者讀取出來.提供了可以存取與機器無關的Java原始類型數(shù)據(jù)(int掌挚,double等)的方法雨席。
public static void main(String args[]){
ByteArrayOutputStream baos = new ByteArrayOutputStream();
//在調(diào)用構造方法時,首先會在內(nèi)存里面創(chuàng)建一個ByteArray字節(jié)數(shù)組
DataOutputStream dos = new DataOutputStream(baos);
//在輸出流的外面套上一層數(shù)據(jù)流吠式,用來處理int舅世,double類型的數(shù)
try{
dos.writeDouble(Math.random());//把產(chǎn)生的隨機數(shù)直接寫入到字節(jié)數(shù)組
ByteArray中
dos.writeBoolean(true);//布爾類型的數(shù)據(jù)在內(nèi)存中就只占一個字節(jié)
ByteArrayInputStream bais = new
ByteArrayInputStream(baos.toByteArray());
System.out.println(bais.available());
DataInputStream dis = new DataInputStream(bais);
System.out.println(dis.readDouble());//先寫進去的就先讀出來,調(diào)用readDouble()方法讀取出寫入的隨機數(shù)
System.out.println(dis.readBoolean());//后寫進去的就后讀出來奇徒,這里面的讀取順序不能更改位置,否則會打印出不正確的結果
dos.close();
bais.close();
}catch(Exception e){
e.printStackTrace();
}
}
打印流
打印流是輸出信息最方便的類缨硝,注意包含字節(jié)打印流PrintStream和字符打印流:PrintWriter摩钙。打印流提供了非常方便的打印功能,
可以打印任何類型的數(shù)據(jù)信息查辩,例如:小數(shù)胖笛,整數(shù)网持,字符串汪诉。
對象流
對象的輸入輸出流的作用: 用于寫入對象 的信息和讀取對象的信息隆嗅。 使得對象持久化。
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;
//創(chuàng)建要寫入磁盤的類毫别,這個類需要實現(xiàn)接口 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.創(chuàng)建目標路徑
File file = new File("C:\\Users\\bg\\Desktop\\objTest.txt");
//2.創(chuàng)建流通道
FileOutputStream fos = new FileOutputStream(file);
//3.創(chuàng)建對象輸出流
ObjectOutputStream objOP = new ObjectOutputStream(fos);
//4.創(chuàng)建類對象帖汞,并初始化
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);
//讀取對象數(shù)據(jù),需要將對象流強制轉(zhuǎn)換為 要寫入對象的類型
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依賴節(jié)點流b泄鹏,應該先關閉處理流a郎任,再關閉節(jié)點流b
- 可以只關閉處理流,不用關閉節(jié)點流命满。處理流關閉的時候涝滴,會調(diào)用其處理的節(jié)點流的關閉方法。
12.多線程
進程與線程
線程的創(chuàng)建
繼承Thread類胶台,實現(xiàn)Runnable接口歼疮,實現(xiàn)Callable接口
1.繼承Thread類
public class ThreadCreateDemo1 {
public static void main(String[] args) {
MyThread thread = new MyThread();
thread.start(); //調(diào)用start()方法啟動線程,線程不一定立即執(zhí)行,CPU安排調(diào)度
}
}
class MyThread extends Thread {//繼承Thread類
@Override
public void run() {//重寫run()方法诈唬,編寫線程執(zhí)行體
super.run();
System.out.println("hellow_world!");
}
}
2.實現(xiàn)Runnable接口
public class ThreadCreateDemo2 {
//創(chuàng)建線程對象韩脏,調(diào)用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創(chuàng)建的線程!");
}
}
上述兩種創(chuàng)建方式,工作時性質(zhì)一樣。但是建議使用實現(xiàn)Runable接口方式铸磅。解決單繼承的局限性赡矢。
3.實現(xiàn)Callable接口
public class ThreadCreateDemo3 implements Callable<Integer>{
// 實現(xiàn)call方法,作為線程執(zhí)行體
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) {
// 創(chuàng)建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){
// 實質(zhì)還是以Callable對象來創(chuàng)建阅仔、并啟動線程
new Thread(task , "callable").start();
}
}
try{
// 獲取線程返回值
System.out.println("callable返回值:" + task.get());
}
catch (Exception ex){
ex.printStackTrace();
}
}
}
實現(xiàn)Callable接口吹散,需要返回值類型
重寫call方法,需要拋出異常
創(chuàng)建目標對象
創(chuàng)建執(zhí)行服務:ExecutorService ser = Executors.newFixedThreadPool(1);
提交執(zhí)行:Future<boolean style="margin: 0px; padding: 0px;"> result1 = ser.submit(t1);</boolean>
獲取結果:boolean r1 = result1.get()
關閉服務:ser.shutdownNow();
總結不過實現(xiàn)Runnable接口與實現(xiàn)Callable接口的方式基本相同八酒,只是Callable接口里定義的方法有返回值空民,可以聲明拋出異常而已。 因此可以將實現(xiàn)Runnable接口和實現(xiàn)Callable接口歸為一種方式。
Runnable界轩、Callable接口的方式創(chuàng)建多線程,所以非常適合多個相同線程來處理同一份資源的情況,如果需要訪問當前線程画饥,則必須使用Thread.currentThread()方法
采用繼承Thread類的方式創(chuàng)建多線程,因為線程類已經(jīng)繼承了Thread類,所以不能再繼承其他父類
生命周期
線程被創(chuàng)建并啟動以后浊猾,它既不是一啟動就進入了執(zhí)行狀態(tài)抖甘,也不是一直處于執(zhí)行狀態(tài)新建(New)、就緒(Runnable)葫慎、運行(Running)衔彻、阻塞(Blocked)和死亡(Dead)5種狀態(tài)
Thread.State:
初始(NEW):新創(chuàng)建了一個線程對象,但還沒有調(diào)用start()方法幅疼。
運行(RUNNABLE):Java線程中將就緒(ready)和運行中(running)兩種狀態(tài)籠統(tǒng)的稱為“運行”米奸。
線程對象創(chuàng)建后,其他線程(比如main線程)調(diào)用了該對象的start()方法爽篷。該狀態(tài)的線程位于可運行線程池中悴晰,等待被線程調(diào)度選中,獲取CPU的使用權逐工,此時處于就緒狀態(tài)(ready)铡溪。就緒狀態(tài)的線程在獲得CPU時間片后變?yōu)檫\行中狀態(tài)(running)。阻塞(BLOCKED):表示線程阻塞于鎖泪喊。
等待(WAITING):進入該狀態(tài)的線程需要等待其他線程做出一些特定動作(通知或中斷)棕硫。
超時等待(TIMED_WAITING):該狀態(tài)不同于WAITING,它可以在指定的時間后自行返回袒啼。
終止(TERMINATED):表示該線程已經(jīng)執(zhí)行完畢
線程的優(yōu)先級
Java提供一個線程調(diào)度器來監(jiān)控程序中啟動后進入就緒狀態(tài)的所有線程哈扮,線程調(diào)度
器按照優(yōu)先級決定應該調(diào)度哪個線程來執(zhí)行。
線程的優(yōu)先級用數(shù)字表示蚓再,范圍從1~10.
hread.MIN_PRIORITY = 1;
Thread.MAX_PRIORITY = 10;
Thread.NORM_PRIORITY = 5;
使用以下方式改變或獲取優(yōu)先級
getPriority() . setPriority(int xxx)
線程方法
1 | public void start() 使該線程開始執(zhí)行滑肉;Java 虛擬機調(diào)用該線程的 run 方法。 |
---|---|
2 | public void run() 如果該線程是使用獨立的 Runnable 運行對象構造的摘仅,則調(diào)用該 Runnable 對象的 run 方法靶庙;否則,該方法不執(zhí)行任何操作并返回娃属。 |
3 | public final void setName(String name) 改變線程名稱六荒,使之與參數(shù) name 相同。 |
4 | public final void setPriority(int priority) 更改線程的優(yōu)先級矾端。 |
5 | public final void setDaemon(boolean on) 將該線程標記為守護線程或用戶線程掏击。 |
6 | public final void join(long millisec) 等待該線程終止的時間最長為 millis 毫秒。 |
7 | public void interrupt() 中斷線程秩铆。 |
8 | public final boolean isAlive() 測試線程是否處于活動狀態(tài)砚亭。 |
9 | public static void yield() 線程禮讓: 暫停當前正在執(zhí)行的線程對象,并執(zhí)行其他線程。 |
10 | public static void sleep(long millisec) 線程休眠: 在指定的毫秒數(shù)內(nèi)讓當前正在執(zhí)行的線程休眠(暫停執(zhí)行)钠惩,此操作受到系統(tǒng)計時器和調(diào)度程序精度和準確性的影響。 |
11 | public static boolean holdsLock(Object x) 當且僅當當前線程在指定的對象上保持監(jiān)視器鎖時族阅,才返回 true篓跛。 |
12 | public static Thread currentThread() 返回對當前正在執(zhí)行的線程對象的引用。 |
13 | public static void dumpStack() 將當前線程的堆棧跟蹤打印至標準錯誤流坦刀。 |
停止線程:jdk提供了stop愧沟,但不建議使用可以自己去停止它
守護(daemon)線程
線程分為前臺線程與后臺線程(用戶線程與守護線程)
虛擬機必須確保用戶線程執(zhí)行完畢
虛擬機不用等待守護線程執(zhí)行完畢
并發(fā),隊列 和 鎖鲤遥,死鎖
同一個對象被多個線程同時操作就是并發(fā)沐寺。
多個線程訪問同一個對象, 并且某些線程還想修改這個對象 .這時候我們就需要線程同步 . 線程同步其實就是一種等待機制 , 多個需要同時訪問此對象的線程進入這個對象的等待池 形成隊列, 等待前面線程使用完畢 , 下一個線
程再使用。
上面的并發(fā)問題我們會加一個鎖(synchronized)來解決盖奈。我鎖上門的時候你們都別進來混坞。但是加上鎖之后會有以下為:
一個線程持有鎖會導致其他所有需要此鎖的線程掛起 ;
在多線程競爭下 , 加鎖 , 釋放鎖會導致比較多的上下文切換 和 調(diào)度延時,引起性能問題 ;
-
如果一個優(yōu)先級高的線程等待一個優(yōu)先級低的線程釋放鎖 會導致優(yōu)先級倒置 , 引起性能問題 .
java 死鎖產(chǎn)生的四個必要條件:
1、互斥使用钢坦,即當資源被一個線程使用(占有)時究孕,別的線程不能使用
2、不可搶占爹凹,資源請求者不能強制從資源占有者手中奪取資源厨诸,資源只能由資源占有者主動釋放。
3禾酱、請求和保持微酬,即當資源請求者在請求其他的資源的同時保持對原有資源的占有。
-
4颤陶、循環(huán)等待颗管,即存在一個等待隊列:P1占有P2的資源,P2占有P3的資源指郁,P3占有P1的資源忙上。這樣就形成了一個等待環(huán)路。
死鎖的情況下如果打破上述任何一個條件闲坎,便可讓死鎖消失疫粥。
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 開始執(zhí)行");
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 開始執(zhí)行");
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 開始執(zhí)行
Tue May 05 10:51:06 CST 2015 LockA 開始執(zhí)行
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 開始執(zhí)行");
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); // 馬上進行嘗試梗逮,現(xiàn)實情況下do something是不確定的
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
class LockBb implements Runnable {
public void run() {
try {
System.out.println(new Date().toString() + " LockB 開始執(zhí)行");
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讓出能執(zhí)行的時間慷彤,否則兩個永遠是死鎖
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
Tue May 05 10:59:13 CST 2015 LockA 開始執(zhí)行
Tue May 05 10:59:13 CST 2015 LockB 開始執(zhí)行
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將花費較少的時間來調(diào)度線程,性能更好跋选。并且具有更好的擴展性(提供更多的子類)
- 優(yōu)先使用順序:
Lock > 同步代碼塊(已經(jīng)進入了方法體涕癣,分配了相應資源)> 同步方法(在方
法體之外)
線程通訊
線程通信的目標是使線程間能夠互相發(fā)送信號。另一方面前标,線程通信使線程能夠等待其他線程的信號坠韩。
線程的通信方式
- volatile
- Wait/Notify機制
- join方式
- threadLocal
- CountDownLatch 并發(fā)工具
- CyclicBarrier 并發(fā)工具
volatile
public class Volatile implements Runnable {
private static volatile Boolean flag = true;
@Override
public void run() {
while (flag) {
System.out.println(Thread.currentThread().getName() + " - 執(zhí)行");
}
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 - 執(zhí)行
Thread-0 - 執(zhí)行
Thread-0 - 執(zhí)行
Thread-0 - 執(zhí)行
Thread-0 - 執(zhí)行
線程結束
**WaitNotify **
public class WaitNotify {
// 狀態(tài)鎖
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 {
// 定義匿名子類創(chuàng)建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;
}
// 線程產(chǎn)生序列號
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();
// 三個線程產(chǎn)生各自的序列號
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: 準備...
全部啟動完畢!
全部啟動完畢!
全部啟動完畢!
線程池
經(jīng)常創(chuàng)建和銷毀、使用量特別大的資源炼列,比如并發(fā)情況下的線程只搁,對性能影響很大。提前創(chuàng)建好多個線程俭尖,放入線程池中氢惋,使用時直接獲取,使用完放回池中目溉∶靼梗可以避免頻繁創(chuàng)建銷毀、實現(xiàn)重復利用缭付。
ExecutorService 和 Executors
ExecutorService:真正的線程池接口柿估。常見子類ThreadPoolExecutor
void execute(Runnable command) :執(zhí)行任務/命令,沒有返回值陷猫,一般用來執(zhí)
行Runnable<t style="margin: 0px; padding: 0px;">Future<t style="margin: 0px; padding: 0px;"> submit(Callable<t style="margin: 0px; padding: 0px;"> task):執(zhí)行任務秫舌,有返回值,一般又來執(zhí)行
Callable</t></t></t>void shutdown() :關閉連接池
Executors:工具類绣檬、線程池的工廠類足陨,用于創(chuàng)建并返回不同類型的線程池
13.注解
Java 注解(Annotation)又稱 Java 標注,是 JDK5.0 引入的一種注釋機制娇未。
作用
不是程序本身 , 可以對程序作出解釋.(這一點和注釋(comment)沒什么區(qū)別)
可以被其他程序(比如:編譯器等)讀取.
可以附加在package , class , method , field 等上面 , 相當于給他們添加了額外的輔助信息
我們可以通過反射機制實現(xiàn)對這些元數(shù)據(jù)的訪問
格式
注解是以"@注釋名"在代碼中存在的
還可以添加一些參數(shù)值 , 例如:@SuppressWarnings(value="unchecked")
內(nèi)置注解
Java 定義了一套注解墨缘,共有 10 個,java7之前3 個在 java.lang 中零抬, 4 個在 java.lang.annotation 中镊讼,后續(xù)增加三個
作用在代碼的注解( java.lang )是
@Override - 檢查該方法是否是重寫方法。如果發(fā)現(xiàn)其父類平夜,或者是引用的接口中并沒有該方法時蝶棋,會報編譯錯誤。
@Deprecated - 標記過時方法忽妒。如果使用該方法玩裙,會報編譯警告兼贸。
@SuppressWarnings - 指示編譯器去忽略注解中聲明的警告。
作用在其他注解的注解(或者說 元注解)是( java.lang.annotation)注解
@Retention - 標識這個注解怎么保存吃溅,是只在代碼中溶诞,還是編入class文件中,或者是在運行時可以通過反射訪問决侈。
@Documented - 標記這些注解是否包含在用戶文檔中很澄。
@Target - 標記這個注解應該是哪種 Java 成員。
@Inherited - 標記這個注解是繼承于哪個注解類(默認 注解并沒有繼承于任何子類)
java7之后增加的注解
@SafeVarargs - Java 7 開始支持颜及,忽略任何使用參數(shù)為泛型變量的方法或構造函數(shù)調(diào)用產(chǎn)生的警告。
@FunctionalInterface - Java 8 開始支持蹂楣,標識一個匿名函數(shù)或函數(shù)式接口俏站。
@Repeatable - Java 8 開始支持,標識某注解可以在同一個聲明上使用多次痊土。
package com.annotation;
//測試內(nèi)置注解
import java.util.ArrayList;
import java.util.List;
//所有類默認繼承Object類
public class Test1 extends Object {
//@Override 表示方法重寫
//--> 查看JDK幫助文檔
//--> 測試名字不同產(chǎn)生的效果
@Override
public String toString() {
return super.toString();
}
//方法過時了, 不建議使用 , 可能存在問題 , 并不是不能使用!
//--> 查看JDK幫助文檔
@Deprecated
public static void stop(){
System.out.println("測試 @Deprecated");
}
//@SuppressWarnings 抑制警告 , 可以傳參數(shù)
//--> 查看JDK幫助文檔
//查看源碼:發(fā)現(xiàn) 參數(shù)類型 和 參數(shù)名稱 , 并不是方法!
@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 注解名 { 定義內(nèi)容 }其中的每一個方法實際上是聲明了一個配置參數(shù).
方法的名稱就是參數(shù)的名稱.返回值類型就是參數(shù)的類型 ( 返回值只能是基本類型,Class , String , enum ).
可以通過default來聲明參數(shù)的默認值如果只有一個參數(shù)成員 , 一般參數(shù)名為value
注解元素必須要有值 , 我們定義注解元素時 , 經(jīng)常使用空字符串,0作為默認值 .
注解參數(shù)的可支持數(shù)據(jù)類型:
1.所有基本數(shù)據(jù)類型(int,float,boolean,byte,double,char,long,short)
2.String類型
3.Class類型
4.enum類型
5.Annotation類型
6.以上所有類型的數(shù)組
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的反射機制的實現(xiàn)要借助于4個類:class肄扎,Constructor,F(xiàn)ield赁酝,Method;
其中class代表的時類對 象犯祠,Constructor-類的構造器對象,F(xiàn)ield-類的屬性對象酌呆,Method-類的方法對象衡载。通過這四個對象我們可以粗略的看到一個類的各個組 成部分。
獲取類的方法
//調(diào)用運行時類本身的.class屬性
Class clazz = String.class;
//通過運行時類的對象獲取
Person p = new Person();
Class clazz = p.getClass();
//通過Class的靜態(tài)方法獲取:體現(xiàn)反射的動態(tài)性
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) -- 獲得使用特殊的參數(shù)類型的公共構造函數(shù)隙袁,
Constructor[] getConstructors() -- 獲得類的所有公共構造函數(shù)
Constructor getDeclaredConstructor(Class[] params) -- 獲得使用特定參數(shù)類型的構造函數(shù)(與接入級別無關)
Constructor[] getDeclaredConstructors() -- 獲得類的所有構造函數(shù)(與接入級別無關)
獲取字段
Field getField(String name) -- 獲得命名的公共字段
Field[] getFields() -- 獲得類的所有公共字段
Field getDeclaredField(String name) -- 獲得類聲明的命名的字段
Field[] getDeclaredFields() -- 獲得類聲明的所有字段
獲取方法的信息
Method getMethod(String name, Class[] params) -- 使用特定的參數(shù)類型痰娱,獲得命名的公共方法
Method[] getMethods() -- 獲得類的所有公共方法
Method getDeclaredMethod(String name, Class[] params) -- 使用特寫的參數(shù)類型,獲得類聲明的命名的方法
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);
//創(chuàng)建這個類的一個對象
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()
}
反射方法執(zhí)行
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{
//正常的調(diào)用
Apple apple = new Apple();
apple.setPrice(5);
System.out.println("Apple Price:" + apple.getPrice());
//使用反射調(diào)用
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));
}
}