文章目錄
對象和類
- 類:我們叫做class。
- 對象:我們叫做Object,instance(實例)。以後我們說某個類的對象,某個類的實例。是一樣的意思。
- 1.對象是具體的事物;類是對對象的抽象;
- 2.類可以看成一類對象的模板,對象可以看成該類的一個具體實例。
- 3.類是用於描述同一類型的對象的一個抽象概念,類中定義了這一類對象所應具有的共同的屬性、方法。
類定義
// 每一個源文件必須有且只有一個public class,並且類名和文件名保持一致!
public class Car {
}
class Teacher{ // 一個Java文件可以同時定義多個class
}
class Student {
}
- 這是一個空類,沒有任何實際意義。所以,我們需要定義類的具體信息。對於一個類來說,一般有三種常見的成員:屬性field、方法method、構造器constructor。這三種成員都可以定義零個或多個。
- 編寫一個簡單類
public class KkStu {
//屬性(成員變量)
int id;
String name;
int age;
//方法
void study(){
System.out.println("我正在學習!");
}
//構造方法
KkStu(){
}
}
屬性(field,或者叫成員變量)
-
屬性用於定義該類或該類對象包含的數據或者說靜態特徵。屬性作用範圍是整個類體。
-
在定義成員變量時可以對其初始化,如果不對其初始化,Java使用默認的值對其初始化。
-
屬性定義格式:
[修飾符] 屬性類型 屬性名 = [默認值] ;
數據類型 默認值 整型 0 浮點型 0.0 字符型 ‘\u0000’ 布爾型 false 所有引用型 null
面向對象的內存分析
-
Java虛擬機的內存可以分爲三個區域:棧stack、堆heap、方法區method area。
-
棧的特點如下:
- 1. 棧描述的是方法執行的內存模型。每個方法被調用都會創建一個棧幀(存儲局部變量、操作數、方法出口等)
- 2. JVM爲每個線程創建一個棧,用於存放該線程執行方法的信息(實際參數、局部變量等)
- 3. 棧屬於線程私有,不能實現線程間的共享!
- 4. 棧的存儲特性是“先進後出,後進先出”
- 5. 棧是由系統自動分配,速度快!棧是一個連續的內存空間! -
堆的特點如下:
- 1. 堆用於存儲創建好的對象和數組(數組也是對象)
- 2. JVM只有一個堆,被所有線程共享
- 3. 堆是一個不連續的內存空間,分配靈活,速度慢! -
方法區(又叫靜態區)特點如下:
- 1. JVM只有一個方法區,被所有線程共享!
- 2. 方法區實際也是堆,只是用於存儲類、常量相關的信息!
- 3. 用來存放程序中永遠是不變或唯一的內容。(類信息【Class對象】、靜態變量、字符串常量等)
public class KkStu {
//屬性field
int id;
String name;
int age;
Computer comp;
//方法
void study() {
System.out.println("學習中...使用電腦" + comp.brand);
}
void play() {
System.out.println("玩耍中...");
}
//程序執行的入口,必須有
public static void main(String[] args) {
KkStu stu1 = new KkStu();
stu1.id = 1010;
stu1.age = 18;
stu1.name = "kk";
Computer c1 =new Computer();
c1.brand = "聯想";
stu1.comp = c1;
stu1.study();
stu1.play();
}
}
class Computer {
String brand;
}
對應內存情況
構造方法
- 構造器也叫構造方法(constructor),用於對象的初始化。構造器是一個創建對象時被自動調用的特殊方法,目的是對象的初始化。構造器的名稱應與類的名稱一致。Java通過new關鍵字來調用構造器,從而返回該類的實例,是一種特殊的方法。
- 聲明格式
[修飾符] 類名(形參列表){ //n條語句}
- 要點:
- 1. 通過new關鍵字調用!!
- 2. 構造器雖然有返回值,但是不能定義返回值類型(返回值的類型肯定是本類),不能在構造器裏使用return返回某個值。
- 3. 如果我們沒有定義構造器,則編譯器會自動定義一個無參的構造函數。如果已定義則編譯器不會自動添加!
- 4. 構造器的方法名必須和類名一致!
構造方法重載
- 造方法也是方法,只不過有特殊的作用而已。與普通方法一樣,構造方法也可以重載。
- 示例
public class User {
int id; // id
String name; // 賬戶名
String pwd; // 密碼
public User() {
}
public User(int id, String name) {
super();
this.id = id;
this.name = name;
}
public User(int id, String name, String pwd) {
this.id = id;
this.name = name;
this.pwd = pwd;
}
public static void main(String[] args) {
User u1 = new User();
User u2 = new User(101, "天使");
User u3 = new User(100, "天使", "123456");
}
}
垃圾回收機制(Garbage Collection)
- Java引入了垃圾回收機制,令C++程序員最頭疼的內存管理問題迎刃而解。Java程序員可以將更多的精力放到業務邏輯上而不是內存管理工作上,大大的提高了開發效率。
垃圾回收原理和算法
- 內存管理
- Java的內存管理很大程度指的就是對象的管理,其中包括對象空間的分配和釋放。
- 對象空間的分配:使用new關鍵字創建對象即可
- 對象空間的釋放:將對象賦值null即可。垃圾回收器將負責回收所有”不可達”對象的內存空間。
- 垃圾回收過程
- 任何一種垃圾回收算法一般要做兩件基本事情:
- 1. 發現無用的對象
- 2. 回收無用對象佔用的內存空間。 - 垃圾回收機制保證可以將“無用的對象”進行回收。無用的對象指的就是沒有任何變量引用該對象。Java的垃圾回收器通過相關算法發現無用對象,並進行清除和整理。
- 任何一種垃圾回收算法一般要做兩件基本事情:
- 垃圾回收相關算法
- 1. 引用計數法: 堆中每個對象都有一個引用計數。被引用一次,計數加1. 被引用變量值變爲null,則計數減1,直到計數爲0,則表示變成無用對象。優點是算法簡單,缺點是“循環引用的無用對象”無法別識別。
- 循環引用示例public class Student { String name; Student friend; public static void main(String[] args) { Student s1 = new Student(); Student s2 = new Student(); s1.friend = s2; s2.friend = s1; s1 = null; s2 = null; } }
s1和s2互相引用對方,導致他們引用計數不爲0,但是實際已經無用,但無法被識別。
- 2. 引用可達法(根搜索算法): 程序把所有的引用關係看作一張圖,從一個節點GC ROOT開始,尋找對應的引用節點,找到這個節點以後,繼續尋找這個節點的引用節點,當所有的引用節點尋找完畢之後,剩餘的節點則被認爲是沒有被引用到的節點,即無用的節點
通用的分代垃圾回收機制
- 分代垃圾回收機制,是基於這樣一個事實:不同的對象的生命週期是不一樣的。因此,不同生命週期的對象可以採取不同的回收算法,以便提高回收效率。我們將對象分爲三種狀態:年輕代、年老代、持久代。JVM將堆內存劃分爲 Eden、Survivor 和 Tenured/Old 空間。
- 垃圾回收過程:
- 1、新創建的對象,絕大多數都會存儲在Eden中,
- 2、當Eden滿了(達到一定比例)不能創建新對象,則觸發垃圾回收(GC),將無用對象清理掉, 然後剩餘對象複製到某個Survivor中,如S1,同時清空Eden區
- 3、當Eden區再次滿了,會將S1中的不能清空的對象存到另外一個Survivor中,如S2,同時將Eden區中的不能清空的對象,也複製到S1中,保證Eden和S1,均被清空。
- 4、重複多次(默認15次)Survivor中沒有被清理的對象,則會複製到老年代Old(Tenured)區中,
- 5、當Old區滿了,則會觸發一個一次完整地垃圾回收(FullGC),之前新生代的垃圾回收稱爲(minorGC)
JVM調優和Full GC
- 在對JVM調優的過程中,很大一部分工作就是對於Full GC的調節。有如下原因可能導致Full GC:
- 1.年老代(Tenured)被寫滿
- 2.持久代(Perm)被寫滿
- 3.System.gc()被顯式調用(程序建議GC啓動,不是調用GC)
- 4.上一次GC之後Heap的各域分配策略動態變化
開發中容易造成內存泄露的操作
-
創建大量無用對象
- 比如,我們在需要大量拼接字符串時,使用了String而不是StringBuilder。
String str = ""; for (int i = 0; i < 10000; i++) { str += i; //相當於產生了10000個String對象 }
-
靜態集合類的使用
- 像HashMap、Vector、List等的使用最容易出現內存泄露,這些靜態變量的生命週期和應用程序一致,所有的對象Object也不能被釋放。
-
各種連接對象(IO流對象、數據庫連接對象、網絡連接對象)未關閉
- IO流對象、數據庫連接對象、網絡連接對象等連接對象屬於物理連接,和硬盤或者網絡連接,不使用的時候一定要關閉。
-
監聽器的使用
- 釋放對象時,沒有刪除相應的監聽器。
-
要點
- 1. 程序員無權調用垃圾回收器。
- 2. 程序員可以調用System.gc(),該方法只是通知JVM,並不是運行垃圾回收器。儘量少用,會申請啓動Full GC,成本高,影響系統性能。
- 3. finalize方法,是Java提供給程序員用來釋放對象或資源的方法,但是儘量少用。
this 關鍵字
- 對象創建的過程和this的本質
- 構造方法是創建Java對象的重要途徑,通過new關鍵字調用構造器時,構造器也確實返回該類的對象,但這個對象並不是完全由構造器負責創建。創建一個對象分爲如下四步:
-
- 1. 分配對象空間,並將對象成員變量初始化爲0或空 - 2. 執行屬性值的顯示初始化 - 3. 執行構造方法 - 4. 返回對象的地址給相關的變量
- this的本質就是“創建好的對象的地址”! 由於在構造方法調用前,對象已經創建。因此,在構造方法中也可以使用this代表“當前對象” 。
- this最常的用法:
- 1. 在程序中產生二義性之處,應使用this來指明當前對象;普通方法中,this總是指向調用該方法的對象。構造方法中,this總是指向正要初始化的對象。
- 2. 使用this關鍵字調用重載的構造方法,避免相同的初始化代碼。但只能在構造方法中用,並且必須位於構造方法的第一句。
- 3. this不能用於static方法中。public class User { int id; //id String name; //賬戶名 String pwd; //密碼 public User() { } public User(int id, String name) { System.out.println("正在初始化已經創建好的對象:"+this); this.id = id; //不寫this,無法區分局部變量id和成員變量id this.name = name; } public void login(){ System.out.println(this.name+",要登錄!"); //不寫this效果一樣 } public static void main(String[] args) { User u3 = new User(101,"天使"); System.out.println("打印高天使對象:"+u3); u3.login(); }
```
結果如圖
- this最常的用法:
static 關鍵字
- 在類中,用static聲明的成員變量爲靜態成員變量,也稱爲類變量。 類變量的生命週期和類相同,在整個應用程序執行期間都有效。它有如下特點:
- 1. 爲該類的公用變量,屬於類,被該類的所有實例共享,在類被載入時被顯式初始化。
- 2. 對於該類的所有對象來說,static成員變量只有一份。被該類的所有對象共享!!
- 3. 一般用“類名.類屬性/方法”來調用。(也可以通過對象引用或類名(不需要實例化)訪問靜態成員。)
- 4. 在static方法中不可直接訪問非static的成員。 - 核心要點:
static修飾的成員變量和方法,從屬於類。
普通變量和方法從屬於對象的。
靜態初始化塊
- 構造方法用於對象的初始化!靜態初始化塊,用於類的初始化操作!在靜態初始化塊中不能直接訪問非static成員。
- 注意事項:
- 靜態初始化塊執行順序(學完繼承再看這裏):
- 1. 上溯到Object類,先執行Object的靜態初始化塊,再向下執行子類的靜態初始化塊,直到我們的類的靜態初始化塊爲止。
- 2. 構造方法執行順序和上面順序一樣!!
- 靜態初始化塊執行順序(學完繼承再看這裏):
- 示例 - static初始化塊
public class User3 { int id; //id String name; //用戶名 String pwd; //密碼 static String major; //專業名稱 static { System.out.println("執行類的初始化..."); major = "網絡工程"; printCompany(); } public static void printCompany(){ System.out.println(major); } public static void main(String[] args) { User3 u3 = new User3(); } }
- 執行結果
參數傳值機制
- Java中,方法中所有參數都是“值傳遞”,也就是“傳遞的是值的副本”。 也就是說,我們得到的是“原參數的複印件,而不是原件”。因此,複印件改變不會影響原件。
- 基本數據類型參數的傳值
- 傳遞的是值的副本。 副本改變不會影響原件。
- 引用類型參數的傳值
- 傳遞的是值的副本。但是引用類型指的是“對象的地址”。因此,副本和原參數都指向了同一個“地址”,改變“副本指向地址對象的值,也意味着原參數指向對象的值也發生了改變”。
- 示例 - 多個變量指向同一個對象
public class User4 { int id; //id String name; //用戶名 String pwd; //密碼 public User4(int id, String name) { this.id = id; this.name = name; } public void testParameterTransfer01(User4 u){ u.name="李四"; } public void testParameterTransfer02(User4 u){ u = new User4(200,"王五"); } public static void main(String[] args) { User4 u1 = new User4(100, "張三"); System.out.println(u1.name); u1.testParameterTransfer01(u1); System.out.println(u1.name); u1.testParameterTransfer02(u1); System.out.println(u1.name); } }
- 執行結果
包
- 包機制是Java中管理類的重要手段。 開發中,我們會遇到大量同名的類,通過包我們很容易對解決類重名的問題,也可以實現對類的有效管理。 包對於類,相當於文件夾對於文件的作用。
package
- 我們通過package實現對類的管理,package的使用有兩個要點:
- 1. 通常是類的第一句非註釋性語句。
- 2. 包名:域名倒着寫即可,再加上模塊名,便於內部管理類。 - 示例 - package的命名舉例
com.sun.test; com.oracle.test; cn.kk.dao.test; cn.kk.dao.view; cn.kk.dao.view.model;
- 注意事項:
- 1. 寫項目時都要加包,不要使用默認包。
- 2. com.dao和com.dao.car,這兩個包沒有包含關係,是兩個完全獨立的包。只是邏輯上看起來後者是前者的一部分。
JDK中的主要包
Java中的常用包 | 說明 |
---|---|
java.lang | 包含一些Java語言的核心類,如String、Math、Integer、System和Thread,提供常用功能。 |
java.awt | 包含了構成抽象窗口工具集(abstract window toolkits)的多個類,這些類被用來構建和管理應用程序的圖形用戶界面(GUI)。 |
java.net | 包含執行與網絡相關的操作的類。 |
java.io | 包含能提供多種輸入/輸出功能的類。 |
java.util | 包含一些實用工具類,如定義系統特性、使用與日期日曆相關的函數。 |
導入類import
- 如果我們要使用其他包的類,需要使用import導入,從而可以在本類中直接通過類名來調用,否則就需要書寫類的完整包名和類名。import後,便於編寫代碼,提高可維護性。
- 注意要點:
- 1. Java會默認導入java.lang包下所有的類,因此這些類我們可以直接使用。
- 2. 如果導入兩個同名的類,只能用包名+類名來顯示調用相關類:
java.util.Date date = new java.util.Date();
- 示例 - 導入同名類的處理
import java.sql.Date;
import java.util.*;//導入該包下所有的類。會降低編譯速度,但不會降低運行速度。
public class Test{
public static void main(String[] args) {
//這裏指的是java.sql.Date
Date now;
//java.util.Date因爲和java.sql.Date類同名,需要完整路徑
java.util.Date now2 = new java.util.Date();
System.out.println(now2);
//java.util包的非同名類不需要完整路徑
Scanner input = new Scanner(System.in);
}
}
靜態導入
- 靜態導入(static import)是在JDK1.5新增加的功能,其作用是用於導入指定類的靜態屬性,這樣我們可以直接使用靜態屬性。
import static java.lang.Math.*;//導入Math類的所有靜態屬性
import static java.lang.Math.PI;//導入Math類的PI屬性
總結
-
面向對象可以幫助我們從宏觀上把握、從整體上分析整個系統。 但是具體到實現部分的微觀操作(就是一個個方法),仍然需要面向過程的思路去處理。
-
類可以看成一類對象的模板,對象可以看成該類的一個具體實例。
-
對於一個類來說,一般有三種常見的成員:屬性field、方法method、構造器constructor。
-
構造器也叫構造方法,用於對象的初始化。構造器是一個創建對象時被自動調用的特殊方法,目的是對象的初始化。構造器的名稱應與類的名稱一致。
-
Java引入了垃圾回收機制,令C++程序員最頭疼的內存管理問題迎刃而解。Java程序員可將更多的精力放到業務邏輯上而不是內存管理工作,大大提高開發效率。
-
this的本質就是“創建好的對象的地址”! this不能用於static方法中。
-
在類中,用static聲明的成員變量爲靜態成員變量,也稱爲類變量。類變量的生命週期和類相同,在整個應用程序執行期間都有效。在static方法中不可直接訪問非static的成員。
-
Java方法中所有參數都是“值傳遞”,也就是“傳遞的是值的副本”。也就是說,我們得到的是“原參數的複印件,而不是原件”。因此,複印件改變不會影響原件。
-
通過package實現對類的管理;如果我們要使用其他包的類,需要使用import導入,從而可以在本類中直接通過類名來調用。