第十天
權限修飾符
public? protected? default? private
同一類? true true true true
同一包 true true true false
子父類(不同包) true true flase false
不同包 true false false false
1.修飾類中的成員(方法和變量)
2.修飾類:public:任何地方都可以訪問,在其他劇中使用需要導包!
default:只有在同一個包中訪問!
權限修飾符:設置成員類的訪問范圍!
創建 Object的對象:
Object是所有類的父類
1.toString():返回對象的地址,如果想換,就重寫 父類的方法
2.equals():比較兩個對象是否是同一個對象!,比較對象的地址!
第十一天
==:比較的是地址(引用數據類型)
equals:比較的是 字符串的值
沒有new對象
字符串保存在字符串常量池(字符串常量區),當你需要給一個字符串變量賦值時候,會先到字符串常量區中查找,如果沒有就創建一個,在將所存的地址賦值給字符串變量!如果有就不在創建,直接將地址給變量。
new對象
字符串也是保存在字符串常量池(常量區),當你需要給一個字符串變量賦值的時候,先到字符串常量池中查找,如果沒有就在常量池中創建一個 ,將值賦值到堆中的對象 ,如果有就不再創建,將值賦值到堆中的對象 。
截取字符串:String str = new String("字符數組char",截取的
開始的值,截取到哪里)
int length()? 獲取字符串的長度
char charAt(int index) 獲取特定位置的字符 (注意角標越界)
int indexOf(String str) 獲取特定字符的位置(overload)
int lastIndexOf(int ch) 獲取最后一個字符的位置
類方法運行 String.valueof(轉化)將其他的類型轉換為字符串
char[] s= 字符串.toCharArray()將字符串轉化為字符數組
s.replace("需要替換的字符","替換的字符"); 替換字符串,返回的是一個新的字符串
s.split(切割的符號)? 切割字符串,返回是String數組
s.substring(截取開始的位置,截取結束的位置)截取字符串
s.toUpperCase()字符轉大寫
s.toLowerCase()字符轉小寫
s.trim()去除空格!
第十二天
StringBuffer :字符串緩存區:存在一個字符數組,默認會有一個大?。?6字符,如果超了就會翻一倍,類似于oc中的 可變數組
StringBuffer:具有容器的特性:
1.增加!
StringBuffer str = new StringBuffer();
str.append("字符串");
2.插入!
str.insert(某一位置插入(后一位),插入的字符串);
3.刪除 !
str.delete(開始刪除的位置(包含起始),結束刪除的位置(不包含結束));
deleteCharAt(刪除指定位置的字符串)
4.修改 !
str.replace(開始的位置,結束的位置,替換的字符串);
5.翻轉字符串!
str.reverse();
6.替換指定位置的字符!
str.setCharAt(指定的位置,替換的字符);
7.截取字符串!
str.substring(開始的位置);
str.substring(開始的位置,結束的位置);
8.查找!
str.indexOf(需查找的字符串);查找小的字符串在大的字符串的位置
str.lastIndexOf(字符串);查找最后一個
str.lastIndexOf(字符串,指定位置);從指定位置開始查找
str.toString()將String變為String
str.charAt(位置)查找指定的字符
StringBuffer 和 StringBulider 的使用方式是一樣的!
相同點:1.兩個類都是字符緩沖類!
2.兩個類的方法一樣
不同點:1.StringBuffer線程使用更安全,效率低;StringBulider線程不安全,效率高
2.StringBuffer是jdk1.0就有了,StringBulider1.5才有,推薦StringBulider
System的使用:
System是系統類,主要用途是用來獲取系統的數據? ,無參且不能被實例化,里面的方法都是靜態方法!
1.復制數組:arraycopy(需要復制的數組對象,復制的起始位置,復制到的數組,復制到數組的起始位置(后一位!),指定數據的長度);
例:
int[] arr = {1,3,4,7,8,9};
int[] arr1 = new int[20];
arr1[0] =4;
arr1[1] = 5;
arr1[2] = 6;
System.arraycopy(arr,3,arr1,3,3);
2.返回以毫秒為單位的當前時間:currentTimeMillis();
3.終止jvm:System.exit(0);其實調用的是Runtime.exit();方法!!
4.運行垃圾回收器:System.gc();
5.確定當前系統的屬性:getProperties();
例:
Properties priperties = System.getProperties();
priperties.list(System.out);
獲取操作系統:以鍵:值? ? 存在
System.out.println(priperties.getProperty("os.name"));
在Java中最少有兩個線程:1.主線程 2.垃圾回收線程
Runtime:該程序代表了運行的環境,在程序中是單例
1.getRuntime():返回當前程序運行的環境變量對象。
例:Runtime run = Runtime.getRuntime();
2.exec():根據指定的路徑來執行對應的可執行文件!
例:Process process = run.exec(路徑);
3.destroy():殺掉子進程的可執行的文件!
例:process.destroy();
4.exit():退出jvm虛擬機!
例:run.exit();
5.freeMemory():返回虛擬機的內存
例:System.out.println(“虛擬機內存還剩:”+run.freeMemory());
maxMemory();試圖使用最大的內存
totalMemory();獲取總內存!
日期類的學習:jdk1.1之前是Date,jdk1.1之后使用的是Calendar類來代替Date
date:獲取系統當前的時間,精確到毫秒
獲取當前時間:Date date = new Date();
1.getDate()返回多少號,棄用
Calendar:是一個抽象的類不能直接創建對象的!
例:Calendar cl = Calendar.getInstance();創建一個日歷單例
System.out.println("年"+cl.get(Calender.YEAR));獲取年份
System.out.println("月"+(cl.get(Calender.MONTH)+1));獲取月份,十二進制 0-11
日+ Calender.DATE 獲取日
星期+? ((Calender.DAY_OF_WEEK)-1);獲取星期,七進制 0-6
時+ Calender.HOUR_OF_DAY;獲取小時
分+ Calender.MINUTE;獲取分鐘
秒+ Calendar.SECOND;獲取秒
System.out.println(cl.getTimeInMillis());返回1970到現在的毫秒數
SimpleDateFormat:時間的打印格式
1.將一個時間通過制定的格式以字符串形式輸出
2.將一個字符串也是以指定的格式轉為時間類型
例:
Date date = new Date();
指定一格式
String formate = "yyyy年MM月dd日? hh:mm:ss";
創建格式化對象:
SimpleDateFormate dateF = new SimpleDateFormate(formate);
String time = dateF.format(date);
打印時間
System.out.println(time);
定義字符串時間要與格式化格式一樣
String dateStr = “時間”;
Date newDate = dateF.parse(dateStr);
System.out.println(newDate);
finalize 是程序或對象在被gc回收時調用此方法
第十三天
Math:數學類
Math.abs(數字):返回絕對值
Math.ceil(數字(duoble))向上取整!
Math.floor(數字(double)):向下取整
Math.round(數字):四舍五入
(int)(Math.random()*范圍):產生隨機數
Random random = new Random(10);產生0-9的隨機數
random.nextInt(10);同上!
Array:用來存同一種數據類型的容器;
Object【】 arr:可以存放任意類型的對象!
1.但比較浪費內存,而且容量是固定的,無法隨元素的變化而變化!
2.存放的數據類型不統一
集合:用來存放對象的容器,對象類型是任意的,長度可變!collection!
集合與數組的區別:
數組和集合類都是容器
數組長度是固定的,集合長度是可變的。數組中可以存儲基本數據類型,集合只能存儲對象數組中存儲數據類型是單一的,集合中可以存儲任意類型的對象。
集合類的特點
用于存儲對象,長度是可變的,可以存儲不同類型的對象。集合中只能存對象!
集合的結構:
collection 接口 根接口 集合的公共都在接口中! 單列集合
----》List 接口 可以有重復元素的集合,有序
List特有的方法: 接口
---->ArrayList:存在一個數組(Object[]),添加、刪除元素慢,查找快
元素在內存中是有序的!默認的數組長度是Object[10];當不夠時會增加為原來的1.5倍,涉及到了增容和數組的拷貝所以較慢
ArrayList的特有方法:
ArrayList list = new ArrayList();
ensureCapacity(容量);手動增加容量
trimToSize();調整容量剛好符合元素的個數
---->LinkedList:添加元素快,刪除、查找慢:鏈式的結構!!
元素在內存中是無序的,但打印是有序的
LinkedList的特有方法:
LinkedList list = new LinkedList();
list.add(對象);
list.addFlist(對象);
list.addLast(對象);
addFlist():添加位于第一個的元素
addLast();添加位于最后一個的元素
----》Set 接口 不可以有重復元素的集合,無序
集合的目的:增刪改查更方便!
例:Collection是接口 需要通過實現類來創建對象,集合只能存對象
Collection? coll = new ArrayList();
增加:
1:add()將指定對象存儲到容器中,add 方法的參數類型是Object便于接收任意對象
coll.add(10(對象));添加指定的對象
2:addAll()將指定集合中的元素添加到調用該方法和集合中
Collection coll1 = new ArrayList<>();
coll1.add("李四");
coll.addAll(coll1);將一個數組中的元素添加到另一個數組中
刪除:
3:remove()將指定的對象從集合中刪除
coll.remove("李四");從集合中刪除指定的對象
4:removeAll()將指定集合中的元素刪除
coll.removeAll(coll1);刪除指定集合中的元素
修改
5:clear()清空集合中的所有元素
coll.clear();清空集合中的所有元素!
判斷
6:isEmpty()判斷集合是否為空
coll.isEmpty();返回boolean值,
7:contains()判斷集合何中是否包含指定對象
coll.contains("元素");查找指定元素返回boolean
如果比較的是自定義對象? 這時候需要重寫equals方法和hashCode方法
其實也是調用可equalse();方法
8:containsAll()判斷集合中是否包含指定集合
其實也是調用可equalse();方法
c.contatinsAll(conll);
獲取:? 9:int size()返回集合容器的大小
coll.size();返回有多少個!
轉成數組10:toArray()集合轉換數組
Object[] o = c.toArray();返回的是數組!
11.保留集合中相同的對象 retainAll()
c.retainAll(c1);保留兩個集合中共有的對象,求交集
遍歷集合元素!
1.將集合變為數組
Object[] o = c.toArray(可以指定對象開始變數組);
for(int i=0;i
System.out.println(o[i]);
}
List特有的方法:List中特有的方法都是通過下標來操作的!
---->ArrayList
---->LinkList
List list = new List<>();
插入
1.add(對象,指定位置);
addAll(指定位置,集合); 插入集合
list.add("張三");
list.add(0,"李四");
集合1.addAll(指定位置,集合2);
返回、獲取
2.get(指定位置)
System.out.println(list.get(位置));
打印集合中的對象
查找
3.IndexOf();查找指定的對象在集合中第一個出現的位置:返回位置
System.out.println("是否存在對象:在什么位置:"+list.indexOf("對象"));
4.lastIndexOf();查找指定的對象在集合中最后一次出現的位置:返回位置
System.out.println("是否存在對象:在什么位置:"+list.lastIndexOf("對象"));
5.listIterator();迭代器:用于操作集合中的元素,增刪,獲取集合中的對象!
使用的注意事項:
1.當用迭代器來操作集合元素時,不要集合來操作元素!java.util.ConcurrentModificationException:不允許在用迭代器時用集合
2.
Iterator:迭代器的超級接口:所有的迭代器都繼承于Iterator
迭代器默認指向第一個元素(指針);當調用next()方法后指針就會下移一位,不再指向第一個!
迭代器中常用的方法:
例:Collection c = new ArrayList<>();
c.add("狗娃");..... 添加元素
Iterator it = c.iterator();
1.hasNext();判斷當前指針是否存在元素,返回boolean
System.out.println("當前指針是否存在元素?"+it.hasNext());
2.next();獲取當前指針指向的元素,當被調用是就會下移
while(it.hasNext()){
System.out.println(it.next());
}
3.remove();移除最后一次操作的元素
it.remove()
ListItreaor:特有的方法
例:List list = new List();
list.add(“”);.....
1.獲取迭代器對象
ListIterator it = list.listIterator();
2.hasPrevious();判斷當前指針上一個是否存在元素
it.hasPrevious();獲取當前指針指向的上一個元素,返回boolean
3.previous();獲取上一個值,指針有上移了一位
it.previous();
倒序遍歷集合中的元素
1.首先需要指針移動到最后一個元素的下方
while(it.hasNext()){
it.next();
}
while(it.hasPrevious()){
System.out.println(it.prevopis());
}
4.previousIndex();返回上一個指針的索引值
System.out.println(it.previousIndex());
5.add();通過迭代器的指針的位置來添加元素,添加到當前的指針
it.add(“對象”);
6.set();替換元素
it.set(對象);,替換當前指針指向的元素,最好不要與add()一起用,當用時需要it.next(),否則指針會不知道指哪里
更新
6.set(指定位置,替換的對象);更換指定位置的對象
list.set(位置,對象);
刪除
7.remove(指定位置);刪除指定索引位置!
list.remove(list.size()-1);
8.List.subList(開始,結束);截取集合的某一段,生成一個新的集合
List.subList(開始,結束)將指定的位置保留為另外一個集合。
List newList = list.subList(開始的位置,結束的位置);
遍歷集合
for(int i = 0;i
System.out.println(list.get(i));
}
第十四天
當想讓對象打印成員變量時可復寫toString()方法!
1.LinkedList:
LinkedList list = new LinkedList();
list.add();.....
list.addFirst(“對象”);添加第一個
list.addLast();添加最后一個
list.getFirst();輸入第一個;
list.getLast();輸出最后一個
list.removeLast();刪除最后一個,返回 最后一個元素
list.removerFirst();刪除第一個,返回 最后一個元素
當集合中沒有元素時會拋出java.util.NosuchElementException這個異常
2.數據節構
1.棧:先進后出
push();當被推入時會被推入到第一的位置!在棧中
list.push(對象);往集合中的堆棧中推入一個對象
pop();當推出時會移除最上一位的對象!在棧中
當push()的被去除完后就會變成ArrayList的順序來----》小的先走,大的后走
list.pop();在集合中的堆棧中推出一個對象
2.隊列:先進先出
offer();按順序推入到集合中
list.offer();添加到最后一個!
poll();推出ArrayList的第一個
list.poll();推出第一個
3.獲取逆序的迭代器:descendingIterator
Iterator it = list.descendingIerator();返回的迭代器時逆序的狀態!
while(it.hasNext()){
System.out.println(it.next());
}
Vector: 描述的是一個線程安全的ArrayList 使用跟ArrayList一樣
Vector與ArrayList的區別:
相同點:底層都是使用Object[]數組來實現
不同點:1.ArrayList是線程不同步,操作效率高!
Vector是線程同步的,操作效率低
2.ArrayList是在jdk1.2出現的,Vector是在jdk1.0出現的!
Set集合:也是接口,繼承與Collection接口
特點:無序的元素不能重復
---->HashSet():
存值原理:存在于哈希表中,會通過調用對象的hashCode(先)和equals(后)方法獲取哈希值,然后用移位的運算,獲取哈希表的位置值!
情況一:如果我計算的位置上沒有任何元素,將對象放在位置上
情況二:如果計算的位置上已經存在對象,這時候就會將要存的對象與已存在的對象作比較,如果equals返回的是true :對象就是重復元素,如果是false就在原來的位置上添加!
哈希表中是以桶式的結構來存放數據,一格可存放多個對象!
例:復寫HashCode(){
如果是string就返回this.成員變量.hashCode()值(成員變量?。?/p>
int就返回this.成員變量
return 自定義的哈希;
}
復寫equals(){
如果是string就返回(this.對比值).equals(obj.成員變量)
int就返回this.成員變量==obj.成員變量
return 自定義的值是否相等
}
例:
Set set = new HashSet();
set.add(對象);.....當添加的元素重復時無法進行添加!返回Boolean值
打印的是無序的?。?/p>
遍歷Hashset()集合
1.集合變為數組遍歷
2.迭代器遍歷(無序:添加的順序和打印出來的順序是不一樣的)
---->TreeSet();使用元素的自然順序對元素進行排序.底層使用二叉樹實現
注意點:存進去的對象需要具備自然排序的特性,取第一個字符根據阿斯特碼(ASCII)來排!
1.往TreeSet添加對象的時候,如果對象有自然排序的特性,就按照這個自然排序進行排序!
2.往TreeSet添加對象時,如果對象本身不具備自然排序的特性,運行直接報錯,如果要儲存對象,那么對象的類必須要實現一個Comparable ? 接口的compareTo方法!
public int compareTo(Object o){
Book b =(Book)o;
return this.對比成員-b.對比的成員;
}
3.往TreeSet添加對象時,如果對象本身不具備自然排序的特性,并且沒有實現Comparable? 接口,那么這個時候就需要創建一個TreeSet類的時候傳入一個比較器。
比較器的定義方式
TreeSet set = new TreeSet(new sunComp(創建比較器));在main或別的類的時候
比較的類(比較器也是一個類)必須與Compartor相接
class sunComp(類名)implements Compartor{
復寫此方法!
public int compare (Object o1,Object o2){
o1,o2集合中的對象:book
Book b1= (Book)o1;
Book b2 = (Book)o2;
return? b1.對比的值-b2.對比的值;
}
}
4.如果類中實現了Comparable的接口,又創建TreeSet時傳入了一個比較器,這時候以比較器為標準
例:
TreeSet set = new TreeSet();
set.add("對象");
會自動的將對象排序!
字符串的比較規則:
1.獲取字符串的第一個值進行比較,通過自然排序比較,如果都是一樣的就比較下一個值還是一樣的就再比較下一個,直到無法再比較
2.這個時候就會比較字符串的長度!
第十五天
二叉樹:紅黑樹規則:左小右大!
泛型:確定集合中只能存放某一種數據類型的對象,jdk1.5之后推出的
優點:1.將運行時的錯誤提前到編譯時。2.避免了無謂的強制轉換!
如果傳進去的是基本數據類型,接收時應用改用它的包裝類來接收!
int--->Integer;
short--->Short;
double--->Double;
float--->Float;
byte--->Btey;
boolean--->Boolean;
long--->Long;
char--->Charactor
ArrayList<這個就是泛型> = new ArrayList<這個也是>();
自定義泛型方法:就是一個數據類型的 占位或一個數據類型變量,一般用T(Typt)或E(Element)來做占位符,占位符可以隨意寫,必須要滿足遵守標識符的命名規范!
注意:1.泛型方法中自定義一個泛型數據類型實在實際參數傳遞時被確定的
2.泛型所用標識符需要符合標識符的命名規范!一般用大寫!
格式:public static T 方法名(T s){
return s;
}
接收:String s = 方法名("傳進去的對象");
定義一個泛型類:
定義格式:
class 類名<聲明自定義的泛型>{}
泛型類的使用注意點:
1.泛型類定義的自定義泛型的類型是在創建泛型類對象時確定的!
2.如果一個自定義泛型類在創建時候沒有指定類型,默認為Object類型!
3.靜態方法是不能夠使用自定義類的泛型,只能在靜態方法中再次寫一個泛型!
泛型接口的定義方式
interface 接口名<聲明自定義的泛型>{}
class 類名 implements 接口名<指定類型>{}
泛型接口注意點:
1.接口上自定義的泛型是在實現接口時被指定的!
2.如果接口上沒有指定接口,默認為Object類型!
3.如果需要創建接口實現類對象是指定數據類型,
那么需要格式:class 類名 <聲明自定義泛型> implements? 接口<聲明自定義泛型>{}
Java可以后臺數據管理也可以前端!!
Map:集合? 接口? 雙列集合? K:V? 鍵值對? OC中的字典很象!
特點:存儲數據是以鍵和值的方式,鍵不允許重復,值是允許重復的!
----->HashMap:基于哈希表,而且是無序的!
使用注意點:
1.鍵可以是任意的對象!,值也可以是任意的對象!
2.Map集合中也可以存集合(嵌套集合)(List、Map);
3.當鍵相同時后面的Value會覆蓋前面的Value
儲存原理:也是使用哈希表來存放!
往HashMap添加元素,首先會調用鍵的HashCode()方法? 獲得一個哈希值,然后經過運算獲取一個位置:情況有:
1.如果位置沒有元素,就直接將元素存放在該位置
2.如果位置上有元素,就會調用元素的equals()方法與這個位置上的元素做比較,如果返回是true那么就被視為相同的鍵 就不存了;false就是不同的鍵 就存該元素
----->TreeMap:二叉樹的結構儲存。特點:以鍵來做自然排序的特性!
使用注意點:
1.往TreeMap添加的元素時,如果元素的鍵具備自然排序,那么就會通過自然排序對元素進行排序!
2.如果不具備自然排序特性,鍵所屬的類必須實現Comparable 接口,把鍵的比較規則定義在compareTo()方法中!
3.如果不具備自然排序特性,也沒有實現Comparable接口,創建TreeMap的時候給其一個比較器
結構:
class 類名 implements Compartor 接口{
如果返回的是負數就是在后面
是1就是在前面
0就是不添加!
}
鍵的比較規則定義在compare方法!
----->HashTable:HashMap一樣的使用,線程安全,訪問比較慢(了解!)
Map常用方法:
Map map = new HashMap();
添加:
put(K,V);
map.put(鍵(可存對象),值(可存對象));
putAll(K,V(集合));A.putAll(B);將B中的元素添加到A中
Map map2 = new HashMap();
map.putAll(map2);
刪除:
clear();
map.clear();清空Map集合中所以的元素!
remove();
map.remove(鍵);通過鍵刪除指定的元素!
獲?。?/p>
get(鍵);
map.get(鍵);通過鍵來獲取值!
size();
map.size();返回map集合里面的元素個數!
獲取鍵的方法
判斷:
isEmpty();判斷集合是否為空!
map.isEmpty();判斷集合中是否為空!false是不為空!
containsKey(K);
map.containsKey(鍵);判斷是否存在某一個鍵!有返回true
containsValue(V);
map.containsValue(值);判斷是否含有某一個值!需要復寫equals()和HashCode()方法!
Map的遍歷方式:
迭代器遍歷:
entrySet();
KeySet();
values();
例:
1.定義Map
Map map = new HashMap();
2.添加元素
map.put("范冰冰","李晨");
3.遍歷
map遍歷元素方式一:keySet();獲取所有的鍵用Set集合來保存!
特點:通過遍歷鍵,通過鍵來取值
Set set = map.keySet();
Iterator it = set.iterator();
while(it.hashNext()){
String key = it.next();
System.out.println("鍵"+key+“:值”+map.get(key));
}
map遍歷元素方式二:values():獲取map集合中所有的值,用Collection集合保存
Collection coll = map.values();
Iterator it = coll.iterator();
while(it.hashNext()){
String value = it.next();
System.out.println(“值:”+value);
}
map遍歷元素方式三:entrySet()
Set> entry = map.entrySet();
Iterator> it = entry.iterator();
while(it.hashNext()){
Map.Entry entry = it.next();
Map.Entry:接口:提供給用戶操作map集合
getKey();獲取鍵
getValue();獲取值
setValue();更改值
System.out.println("鍵:"+entry.getKey()+"值:"+entry.getValue());
}
Collection:接口
Collections:集合的工具類
Collection與Collections的區別?
Collection 是一個單列集合的根接口,Collections是操作集合的工具類!只能操作list集合 !
Collections中常有的方法
Collections:常見方法:
Collections.sort();排序
1, 對list進行二分查找:
前提該集合一定要有序。
int binarySearch(list,key);
Collections.binarySearch(集合,"對象");返回索引的位置? 沒找到返回是一個負數,索引
//必須根據元素自然順序對列表進行升級排序
//要求list 集合中的元素都是Comparable 的子類。
int binarySearch(list,key,Comparator);
2,對list集合進行排序。
sort(list);
//對list進行排序,其實使用的事list容器中的對象的compareTo方法
sort(list,comaprator);
//按照指定比較器進行排序
3,對集合取最大值或者最小值。
max(Collection)
max(Collection,comparator)
min(Collection)
min(Collection,comparator)
4,對list集合進行反轉。
reverse(list);
5,對比較方式進行強行逆轉。
Comparator reverseOrder();
Comparator reverseOrder(Comparator);
6,對list集合中的元素進行位置的置換。
swap(list,x,y);
7,對list集合進行元素的替換。如果被替換的元素不存在,那么原集合不變。
replaceAll(list,old,new);
8,可以將不同步的集合變成同步的集合。
Set synchronizedSet(Set s)
Map synchronizedMap(Map m)
List synchronizedList(List list)
9.如果想要將集合變數組:
可以使用Collection 中的toArray 方法。注意:是Collection不是Collections工具類
傳入指定的類型數組即可,該數組的長度最好為集合的size。
第十六天
進程:就是正在運行的程序,作用是分配內存讓應用程序能夠運行!
windows系統號稱多任務(可以同時運行多個應用程序)。
宏觀上:windows確實是運行的多個程序
微觀上:CPU快速作了切換操作,肉眼不可察
線程:線程在一個進程中負責代碼的執行!就是一個進程的執行路徑!
Java程序在運行的時候,jvm會幫我們創建一個主線程和一個垃圾回收器(也是一個線程)。主線程主要負責main方法中的代碼執行。垃圾回收器負責垃圾回收!main是主線程!
一個Java程序中至少有兩個線程。
多線程:在一個進程中有多個線程存在并且線程“同時”執行不同的任務。“同時”:單核CPU快速切換多個線程執行任務,速度很快,肉眼不可察
好處:1.可以解決一個進程中同時執行多個任務的問題
2.可以提高資源的利用率
壞處:1.增加CPU的負擔!不是線程越多越好
2.降低進程中線程的執行效率。
3.會引發線程安全問題
4.容易出現死鎖
Java創建線程:
方式一:Thread(線程類)
1.需要定義一個類來繼承Thread類
2.重寫Thread類中的run方法!把自定義線程的任務代碼寫在run方法中。每一個線程都有自己的任務代碼,jvm創建主線程的任務代碼就是main,自定義想成的任務代碼就寫在run方法中!
class 類名 extends Thread{
public void run(){
//自定義線程的任務代碼
for(int i=0;i<100;i++){
System.out.println(“多線程”+i);
}
}
}
3.創建Thread的子類,并且調用start開啟線程!
在main中開啟線程,
注:1.在main中代碼的執行是從上到下,必須放在執行main的上面
2.一旦線程開啟,就會默認執行線程對象的run方法,但是直接千萬不要調用,如果直接調用run方法就跟普通方法沒有區別!
類名 名字 = new 類名();
名字.start();
for(int i=0;i<100;i++){
System.out.println(“主線程”+i);
}
方式二:推薦!由于Java是單繼承,所以就不能繼承Thread和該繼承的類,但接口可以有多個!
1.自定義一個類實現Runable接口,接口就會提供一個run方法!
2.實現Runable接口中的run方法。將線程中的任務寫在run方法中
3.實現Runable接口的實現類對象!
4.創建一個Thread對象
5.調用Thread的對象的start方法!
class 類名 implements Runnable{
public void run(){
}
}
main函數:
類名 對象名 = new 類名();-----》不是線程對象!只有是Thread或者是Thread的子類才是線程對象!
Thread thread = new Thread(對象名);
thread.start();
為什么要將Runable接口實現類的對象作為參數傳遞?
讓對象中的run方法能夠在線程中的run方法中執行!
線程的生命周期:-
---->CPU的等待資格
---->CPU的執行權
1.創建一個線程:new 子線程類
2.等待運行狀態,改狀態只有CPU的等待資格:
3.運行狀態,改狀態具備了CPU的執行權與CPU的等待資格
4.線程結束,
線程的阻塞狀態:可由運行狀態轉化,一個線程執行了sleep或者wait方法,線程會處于阻塞狀態!如果是sleep方法,如果超過了睡眠時間線程會立馬進入等待運行狀態!如果是wait,就需要通過其他的線程來喚醒!
線程中常用的方法:
1.Thread(String name)初始化線程名字
main 函數中
類名 對象名 = new 類名(“線程名”);
對象名.setName(“更換線程名”);
Thread maint = Thread.currentThread();CPU當前執行的線程
System.out.println("獲取線程的優先級"+maint.getPriority());
定義一個構造方法來調用父類的構造方法
public 類名(String name){
super(name);
}
類中重寫run方法
public void run(){
執行的代碼!睡眠后打印線程名!
try{異常只能處理:Thread父類的sleep方法沒有拋出異常所以子類也不允許拋
this.sleep(毫秒);或Thread.sleep(“毫秒”);可以拋異常
}catch(異常){
。。。。
}
System.out.println(“線程名”+this.getName());
}
2.getName()返回線程的名字
3.setName(String name)設置線程對象名
4.sleep(毫秒)線程睡眠是靜態的可以通過類方法調用!誰執行的Thread.sleep就睡眠誰
5.getPriority()返回線程對象的優先級默認是5,最高是10
6.Thread.currentThread();
CPU當前執行的線程,指的是正在執行的run方法的線程對象
this不是線程對象!
7.setPriority(10);給該對象設置線程的優先級
線程安全問題:
Java的線程鎖:
方式一:同步代碼塊??!推薦
Java中的任意一個對象都會有一個對象的狀態,就可以通過對象的狀態作為鎖的標識符
優點:1.同步代碼塊的鎖對象可以由自己指定,同步函數是固定的的
2.同步代碼塊可以控制被同步的范圍,同步函數必須是整個函數!
注意:1.任意對象都可以做鎖對象
2.如果在同步代碼塊中調用sleep方法,鎖對象不會被釋放!
3.只有真正存在線程安全的時候才會需要使用同步代碼塊,否則會降低效率
4.多線程操作鎖對象必須是唯一的,否則無效
static Object? o = new Object(); 鎖的對象應該是同一個對象static 或 單例
synchronized(鎖對象 o){鎖對象可以使任意Java中的對象,也可以是字符串
代碼!??!
}
方式二:同步函數,用關鍵字sybnchronized修飾函數
同步函數是否有鎖對象:有!
同步函數使用注意點:
1.如果是一個非靜態的函數,同步函數的鎖就是調用方法的對象(this對象),如果是一個靜態函數同步函數的鎖對象是當前函數所屬類的字節碼文件(Class對象)!
2.同步函數的鎖對象固定,不能夠自己來指定
3.同步函數是同步整個函數的代碼塊!
線程什么時候安全什么時候不安全?
出現線程安全的根本原因:
1.存在兩個或兩個以上的線程,并且線程之間共享著一個資源
2.多個語句同時操作的共享資源!
代碼同步是可以解決線程安全問題!如果使用不當會導致線程的死鎖!
A線程等B線程 ,B線程又在等A線程 結果兩個人就一直等下去,這時候就造成了線程的死鎖
線程死鎖不一定會出現,有可能出現!
死鎖的解決方法:盡量避免出現線程之間的等待問題!
public void run() {
if("張三".equals(Thread.currentThread().getName())){
synchronized ("遙控器") { //鎖對象就鎖住了
System.out.println("張三拿到了遙控器 ,準備去拿電池");
synchronized ("電池") {//已經被鎖住了
System.out.println("張三拿到了遙控器和電池,開著空調,在也 不冷了");
}
}
}else if("老王".equals(Thread.currentThread().getName())){
synchronized ("電池") { //鎖也被鎖住了? 電池對象的狀態 變為鎖住的狀態
System.out.println("老王拿到電池,準備去拿遙控器");
synchronized ("遙控器") { //遙控器也被鎖住了
System.out.println("老王拿到了遙控器和電池,開著空調,在也 不冷了");
}
}
}
}
線程的通訊:一個線程完成自己的任務,去通知另外一個線程去完成另外一個任務!
wait();等待如果線程執行了wait()方法,那么該線程就會處于等待狀態,等待狀態的線程必須通過其他線程來調用notify()方法來喚醒!通過鎖對象來調用
try{
鎖對象.wait();
}catch(){
}
notify();喚醒? 隨機喚醒一個
鎖對象.notify();
notifyAll();喚醒全部線程
生產者和消費者
注意:1.wait和notify都是屬于Object對象的
2.wait方法和notify方法必須在同步線程中使用
3.wait方法和notify方法必須由鎖對象來調用!
線程的停止:
1.停止一個線程 一般通過一個變量來控制
2.如果需要停止一個處于等待狀態的線程,應配合interrupt方法!
Thread.currentThread().stop();停止當前前程!已過時!此方法不安全
對象.interrupt();直接調用interrupt是沒有用的
如何創建后臺(守護)線程:在一個進程中只剩下后臺(守護)線程,守護線程也會死掉!
線程默認的不是后臺線程!將某個對象的線程標記為守護線程
對象.setDaemon(布爾值);true是守護 false一般線程
join方法 加入:可以在一個線程中,加入另外一個線程執行,前面的線程會等待加入的線程執行完畢后在執行!
run方法中加入另外一個線程
類名 對象名 = new 類名();
對象.start();
try{如果有一個線程執行join語句,有一個新的線程加入,在執行的線程需要讓步新的線程執行完畢,然后才能執行
對象.joun();
}catch(){
}