一、 類和對象
1.1 面向對象與面向過程的差別
1.面向過程
采用面向過程必須了解整個過程,每個步驟都有因果關系,每個因果關系都構成了一個步驟,多個步驟就構成了一個系統,因為存在因果關系每個步驟很難分離,非常緊密,耦合度高,當任何一步驟出現問題,将會影響到所有的系統。如:采用面向過程生産電腦,那麼他不會分CPU、主機闆和硬碟,它會按照電腦的工作流程一次成型。
2.面向對象
面向對象對會将現實世界分割成不同的單元(對象),實作各個對象,如果完成某個功能,隻需要将各個對象協作起來就可以。
1.2 面向對象的三大特征
1. 封裝
2. 繼承
3. 多态
1.3 類與對象的概念
類是對具有共性事物的抽象描述,是在概念上的一個定義,那麼如何發現類呢?通常根據名詞(概念)來發現類,如在成績管理系統中:學生、班級、課程、成績
學生—張三
班級—Java1059
課程—JavaSE
成績—張三成績
以上“張三”、“Java1059”、“JavaSE”和“張三的成績”他們是具體存在的,稱為對象,也叫執行個體,就是說一個類的具體化(執行個體化),就是對象或執行個體。
為什麼面向對象成為主流技術,主要就是因為更符合人的思維模式,更容易的分析現實世界,是以在程式設計中也采用了面向對象的技術,從軟體的開發的生命周期來看,基于面向對象可以分為三個階段:
OOA(面向對象的分析) Object-OrientedAnalysis
OOD(面向對象的設計) Object-OrientedDesign
OOP(面向對象的程式設計) Object Oriented Programming
Java 就是一個純面向對象的語言
我們再進一步的展開,首先看看學生:
學生:學号、姓名、性别、位址,班級
班級:班級代碼、班級名稱
大家看到以上我們分析出來的都是類的屬性
接下來采用簡易的圖形來描述一下,來描述我們的概念

通過以上分析,大家應該了解:
類=屬性+方法
屬性來源于類的狀态,而方法來源于動作
以上模型完全可以使用面向對象的語言,如Java 來實作
1.1 如何定義一個類
在Java中如何定義一個類!格式如下。
[類的通路修飾符] class 類名 extends 父類名稱 implements 接口名稱{
類體:包括下面兩方面
屬性
方法
}
定義學生類型Student,是對現實中的學生的一種抽象,是不存在的,是概念上的定義。注意:Student是一個類,是引用類型。
public class Student{
// 屬性
// 學生學号
// 成員變量,非靜态變量
// 成員變量是對象級别的,必須先存着對象才有意義,才能通路
// 成員變量不能使用類名.的方式進行通路
int id;
// 學生姓名
String name;
// 學生性别
String sex;
// 學生年齡
int age;
// 家庭住址
String address;
// 方法
public void study(){
System.out.println("學習Java ");
1.2 如何建立一個對象
在Java中如何建立一個對象呢!必須使用new關鍵字,當對象建立完成後,對于成員變量或成員方法才可以通路或調用,下面的例子。
public class ClassTest01 {
public static void main(String[] args){
// 建立對象
// 注意:stu1對象是局部變量,是Student01類型的局部變量
// 變量stu1不是基本資料類型,是引用類型
// 引用類型儲存的是對象在堆記憶體中的位址
// 我們通過這個引用來通路堆記憶體中的對象
Student01 stu1 = new Student01();
// 一個類可以建立多個對象
// 可以說,所有的學生都可以是Student01類的對象
Student01 stu2 = new Student01();
// 使用對象通路成員屬性
// 通路成員屬性,必須使用引用.的方式進行通路
// 注意:成員變量不能采用類名.的方式進行通路
System.out.println("id: " + stu1.id);
System.out.println("name: " + stu1.name);
System.out.println("age: " + stu1.age);
System.out.println("sex: " + stu1.sex);
System.out.println("address: " + stu1.address);
// 使用對象為成員變量進行指派
// 成員變量的讀取和指派都是采用引用.的方式
stu1.id = 1001;
stu1.name = "張三";
stu1.age = 21;
stu1.sex = "男";
stu1.address = "太原小店區";
// 通過對象讀取成員變量的值
}
class Student01{
int id;
String name;
int age;
String sex;
String address;
一個類可以建立多個個對象,成員變量隻屬于目前的對象(每個對象擁有自己的成員屬性,是以成員屬性隻屬于特定的對象,不屬于類),是以隻有通過對象才可以通路成員變量,通過類不能直接通路成員變量。
注意:stu1隻是引用,指向堆記憶體中對象的位址,我們隻能通過這個引用對記憶體中的對
象進行通路。每一個對象會在記憶體中開創一塊空間,每個對象的屬性也隻能屬于自己的
對象,是以成員變量是屬于對象的或執行個體的。
上面程式存在缺點,所有的成員屬性全部公開,如:年齡可以指派為負數!如何才能控制成員屬性不能随意的指派!
1.1 面向對象的封裝性
如何對成員屬性的值進行保護和限制,使成員變量的更安全!通過下面的例子說明。
public class ClassTest02 {
Student02 stu1 = new Student02();
// 讀取成員變量
//stu1.age = 21;
// 在程式中對age屬性沒有任何限制,外部程式可以随意通路屬性age,
// 導緻age不安全。這種可以随意修改或指派不合法的資料可能造成程式的
// 安全隐患,如何才能有效的限制不合法的資料呢!
stu1.age = -1;
class Student02{
在上面的例子中可以看到,程式中沒有對成員屬性進行任何的保護,可以随意的通路,并且将非法的值随意的賦給成員變量,這可能造成整個程式的執行結果發生錯誤。如何才能對成員變量進行保護呢!可以為每個成員變量的通路權限進行限制,在每個成員屬性前加上通路修飾符private,這樣就隻有在本類中才可以通路成員屬性,類的外部是不能直接對成員屬性進行通路的,這樣成員屬性就受到保護了,如下例子。
public class ClassTest03 {
Student03 stu1 = new Student03();
// 編譯不能通過,成員屬性通路受到限制
// System.out.println("age: " + stu1.age);;
// stu1.age = -1;
// 編譯不能通過,成員屬性通路受到限制
// System.out.println("age: " + stu1.age);
class Student03{
private int id;
private String name;
private int age;
private String sex;
private String address;
通過對屬性通路進行限制,在類的外部不能直接操作成員屬性了,成員屬性更安全了,但是,如果不能對成員屬性進行通路,實際上這個類對其他類來說就無意義了,是以,既能通路成員變量,又能對成員屬性進行保護,如何才能實作呢!那就是封裝性的第二個辦法了,為每個成員屬性建立兩個公共的方法,一個可以為成員屬性進行指派,另一個可以擷取成員屬性的值,如下例子。
public class ClassTest04 {
Student04 stu1 = new Student04();
stu1.setId(1001);
stu1.setName("張三");
stu1.setAge(-21);
stu1.setSex("男");
stu1.setAddress("太原小店區");
System.out.println("id: " + stu1.getId());
System.out.println("name: " + stu1.getName());
System.out.println("age: " + stu1.getAge());
System.out.println("sex: " + stu1.getSex());
System.out.println("address: " + stu1.getAddress());
class Student04{
public int getId() {
return id;
public void setId(int id) {
this.id = id;
public String getName() {
return name;
public void setName(String name) {
this.name = name;
public int getAge() {
return age;
public void setAge(int age) {
if(age >= 0 && age <= 100){
this.age = age;
}else{
System.out.println("非法資料!");
this.age = 0;
}
public String getSex() {
return sex;
public void setSex(String sex) {
this.sex = sex;
public String getAddress() {
return address;
public void setAddress(String address) {
this.address = address;
從上面的示例可以看出,采用方法可以控制指派的過程,加入了對年齡的檢查,避免了外部直接操縱成員屬性,這就是封裝,封裝其實就是封裝屬性,讓外界知道這個類的狀态越少越好。屬性隻能通過方法通路,在方法中對屬性的值進行限制就是封裝。通過方法我們就可以控制對内部狀态的讀取權利。封裝屬性,公開方法。是以,将屬性私有化,并且提供公共的方法對屬性進行通路就是Java的封裝。
提供的方法有setter和getter兩種,setter方法是給屬性指派的,而getter方法是取得屬性值的。關于這兩個方法是有規範的,setter方法是:set + 屬性名稱(第一個字母大寫)。getter方法是:get + 屬性名稱(第一個字母大寫)。
1.1 類的構造函數
構造函數也稱作構造方法或構造器,英文為Constructor。構造方法也是一個類的組成部分,文法格式如下。
[方法通路修飾符清單] 構造方法名(形式參數清單){
方法體;
如何調用構造方法!
隻能通過new關鍵字調用構造方法,調用會建立類的執行個體,過程是在堆記憶體開辟空間并儲存類的執行個體。
回顧:靜态方法必須使用類名.的方式進行調用。
成員方法必須使用引用.的方式進行調用。
第一個例子:建立一個類的執行個體或對象時實際上是調用了類的構造方法。
//構造方法
public Student04() {
System.out.println("建立一個類的" +
"執行個體或對象時實際上是調用了類的構造方法");
構造方法修飾詞清單:public、protected和private。
第二個例子:構造方法的通路修飾符的調用限制,public和private的差別。
public class ClassTest05 {
//不能編譯通過,構造方法被private修飾通路不到
//Student05 stu2 = new Student05();
class Student04{
System.out.println("構造方法被public修飾");
class Student05{
private Student05() {
System.out.println("構造方法被private修飾");
構造方法和普通方法一樣可以重載。因為形式參數清單可以有零到多個參數。
第三個例子:構造方法可以重載,可以為不同的成員屬性進行賦初值。
public class ClassTest06 {
Student06 stu1 = new Student06();
Student06 stu2 = new Student06(1001,"張三");
class Student06{
public Student06() {
//含參數的構造方法
public Student06(int id, String name) {
構造方法的特點:
1. 構造方法的名稱必須和類名相同。
2. 構造方法不能存在傳回值。構造方法不具有任何傳回值類型,即沒有傳回值,關鍵字void也不能存在,如果存在void關鍵字就不是構造方法了,就是普通方法了。
3. 任何類都有構造函數,如果一個類沒有顯示的定義構造函數,系統會為該類定義一個預設的構造器,這個構造器不含任何參數(無參的構造函數),如果在類中顯示的定義了構造器(有參數的或無參數的構造方法),系統就不會再建立預設的無參構造器,如果需要必須自己建立。通常情況下,如果在類中手動添加了帶參數的構造函數,那麼也會手動的添加一個無參數的構成方法。
第四個例子:如果類沒有顯示的構造方法,系統會預設建立一個無參數的構造方法,如果顯示的建立了一個構造方法,系統則不會在建立預設的無參數構造方法。
構造方法的作用是什麼!
1. 建立對象。
2. 建立對象時為成員屬性賦初值。
成員屬性的指派時機!
成員屬性聲明後不用手動賦初值既可以使用,因為成員屬性必須執行個體進行調用,在建立執行個體或對象時,必須通過new 構造方法()的方式建立對象,系統會為成員屬性賦初值。
如果調用無參數的構造方法,系統會為成員屬性賦預設值,也可以通過帶參數的構造方法在建立對象的時候為成員變量賦初值。
第五個例子:通過構造函數為成員屬性進行賦初值。
1.1 類的執行個體和引用
1.1.1 Java中的記憶體概述
Java虛拟機啟動後的記憶體概述。
1.1.1 Java執行個體的建立
Student類:
public class Student {
private String id;
private String name;
private int age;
private String sex;
public static void main(String[] args){
//建立st對象
Student st = new Student();
//st對象成員變量指派
st.id = "1001";
st.name = "張三";
st.age = 23;
st.sex = "男";
第一步,JVM調用執行main 方法,将main 方法壓入棧,然後new Student 對象
//建立一個對象
Student st = new Student();
第二步,對st執行個體的屬性進行指派
st.id = "1001";
st.name = "張三";
st.age = 23;
st.sex = "男";
1.1.1 執行個體引用為null時出現情況
一個類的執行個體必須通過new關鍵字建立後才能通路成員屬性或成員方法,如果對象沒有建立即調用成員,會出現異常現象,下面通過例子說明。
第六個例子:如果不使用new關鍵字建立對象即使用引用.的方式通路成員屬性或成員方法出現的問題。
注意:當一個對象沒有引用指向他,則這個在記憶體中的對象将成為垃圾,等待垃圾回收器回收。
記憶體說明:
1.1 參數傳遞
在程式執行過程中可以進行參數的傳遞,參數類型分為基本資料類型和引用類型,下面分别進行說明。
1.1.1 參數類型為基本資料類型
參數的類型是基本資料類型,那麼傳遞的是基本資料類型的值。形參的變量是局部變量,那麼傳遞的值就賦給這個局部變量,在方法中對這個局部變量如何操作都不會影響都調用處變量的值。下面通過一個例子說明。
例1:ParameterTest01
public class ParameterTest01 {
int i = 10;
add(i);
System.out.println("調用處局部變量i的值:" + i);
public static void add(int i){
i *= 2;
System.out.println("局部變量i的值: " + i);
值傳遞:
1.1.1 參數類型為引用資料類型
參數是引用類型,傳遞的是對象的引用,也就是對象在堆記憶體中的位址。那麼通過參數操作實際上是操作的是記憶體中的對象。下面通過一個例子說明。
例2:ParameterTest02
public class ParameterTest02 {
Student stu = new Student("張三",20);
modify(stu);
System.out.println("學生姓名:" + stu.name + " 年齡:" + stu.age);
public static void modify(Student stu){
stu.name = "李四";
stu.age = 23;
class Student{
public String name;
public int age;
public Student(String name,int age){
this.age = age;
通過一張圖說明:
引用類型的參數傳遞的是對象在記憶體中的位址,實際上操作的是記憶體中的對象,是以對象的屬性值被modify()方法改變了。
1.1 Java中的this關鍵字
this關鍵字在Java的類中代表什麼含義!
1.this是一個引用,指向目前對象在記憶體中的引用(位址)。就是目前對象。
2.建立的每一個Java對象中都有一個this引用。
3.this引用儲存的是目前對象在記憶體中的位址,每一個Java對象中都有一個this引用指向對象自身。
第一個例子:建立的對象在記憶體中,每個對象中都有一個this引用儲存的是目前對象的記憶體位址。
//建立str1對象
Student str1 = new Student();
// str1對象成員變量指派
str1.id = "1001";
str1.name = "張三";
str1.age = 23;
str1.sex = "男";
//建立str2對象
Student str2 = new Student();
// str2對象成員變量指派
str2.id = "1002";
str2.name = "李四";
str2.age = 22;
str2.sex = "女";
用圖說明:
this關鍵字能用在哪些地方!
1.this可以用在成員方法中,代表目前對象。this儲存目前對象的引用,指向目前對象在記憶體中的位址。
第二個例子:this關鍵字用在成員方法中。(this關鍵字不能用在靜态方法中)
public class ClassTest06 {
public static void main(String[] args){
// 建立對象
Student06 stu1 = new Student06();
Student06 stu2 = new Student06(1001,"張三");
stu2.method();
class Student06{
private int id;
public Student06() {
public Student06(int id, String name) {
this.id = id;
this.name = name;
//成員方法
public void method(){
this.id = 8888;
System.out.println(id);
第三個例子:this關鍵字可以區分成員屬性和局部變量。
stu1.method();
String name = "局部變量";
this.name = "成員屬性";
System.out.println("name:" + name);
System.out.println("this.name" + this.name);
第四個例子:this關鍵字不能使用在靜态方法中,因為靜态方法的執行根本不需要Java對象的存在。直接使用類名.的方式通路。而this代表的是目前對象。是以在靜态方法中根本就沒有目前對象。
2.this可以用在構造方法中,用來調用目前類中其他的構造函數。
文法:this(實參);
通過在構造方法調用目前類中另一個構造方法來實作代碼重用。
注意:通過this(實參);調用目前類中其他構造函數的語句必須出現在構造方法的第一
行。否則編譯不能通過。
第五個例子:this關鍵字用在構造方法中用來調用其他的構造方法,好處是代碼可以重用。但是要注意,如果在構造函數中通過this調用其他的構造函數,this語句必須是構造方法中的第一行。
System.out.println(stu1.id + stu1.name);
this(1001,"張三");
//帶參構造
1.1 Java中的static關鍵字
在Java中,static修飾符可以修飾:變量、方法和代碼塊。
1.static修飾的變量叫做靜态變量。
2.static修飾的方法叫做靜态方法。
3.static還可以定義靜态語句塊。
1.1.1 靜态語句塊
用static 修飾的變量和方法,可以采用類名.的方式進行通路。
用static聲明的代碼塊為靜态代碼塊,執行時機是類加載階段,在程式入口程式main()方法執行之前執行,隻執行一次,是自上而下的順序執行(常用于初始化工作,建立對象)。
靜态語句塊的文法格式:
static{
語句塊;
第一個例子:建立靜态語句塊,靜态語句塊在類加載時執行,隻執行一次。
因為靜态變量、靜态語句塊在類加載階段執行,是以,變量和語句塊聲明的順序是有關系的。
第二個例子:靜态變量和靜态語句塊的聲明順序
1.1.2 執行個體語句塊
執行個體語句塊,每一次調用構造方法之前會執行一次。執行個體語句塊執行順序也是自上而下。
執行個體語句塊文法格式:
{
第三個例子:建立執行個體語句塊。
1.1.3 靜态方法
static修飾的方法叫做靜态方法,也成為類方法。通常情況下工具類中的方法大部分都是靜态方法,不需要建立對象既可以直接以類名.的方式進行通路,不需要建立對象。
第三個例子:靜态方法使用類名.的方式進行通路,靜态方法中可以直接調用靜态屬性,靜态方法中不能通路成員,如果要通路成員需要建立執行個體,是以在靜态方法中沒有this的概念。
1.1.4 靜态變量
static修飾的變量叫做靜态變量,也成為類變量。
在什麼情況下将變量聲明成靜态變量!如果這個屬性所有的對象都有,并且這個屬性的值是相同的,具有共性的屬性值的屬性,則該屬性聲明成靜态的屬性。
第四個例子:靜态變量在方法區,不在堆區。所有的對象共享。
成員變量在建立Java對象的時候初始化。而靜态變量在類加載階段指派,并且隻指派一次。因為隻儲存一份。
1.1.5 靜态變量和靜态塊的執行順序
在Java中,隻有靜态變量和靜态塊在代碼中是有順序的,成員方法是沒有順序的。
第五個例子:靜态屬性和靜态塊的執行順序問題。
總結:我們已經知道在一個類中,可以存在靜态方法、靜态變量、成員方法、成員屬性、構造方法、靜态語句塊、執行個體語句塊和this關鍵字。
1.2 單例模式
單例模式是種設計模式中最簡單的一種設計模式。是較為常見的一種設計模式,單例模式就是在任何時候都保證某個Java類的執行個體隻有一個,主要是為了節省記憶體空間。通常情況下,工具類一般采用單例模式,就一個對象,所有對這個工具類中的成員方法的調用都是通過這個唯一的執行個體調用的。
如何實作單例模式!實作單例模式的原則:
1.構造方法私有化。
2.對外提供一個公開的靜态的擷取目前類型對象的方法。
3.提供一個目前類型的私有的靜态變量。
單例模式分為兩種模式:
餓漢式單例:在類加載階段就建立了對象。
懶漢式單例:用到對象的時候才會建立對象。
第一個例子:建立一個餓漢式單例模式。
package weixin911;
/*
* 單例模式:保證類在記憶體中隻有一個對象。
*
* 如何保證類在記憶體中隻有一個對象呢?
* A:把構造方法私有
* B:在成員位置自己建立一個對象
* C:通過一個公共的方法提供通路
*/
public class StudentDemo {
//餓漢式
public static void main(String[] args) {
// Student s1 = new Student();
// Student s2 = new Student();
// System.out.println(s1 == s2); // false
// 通過單例如何得到對象呢?
// Student.s = null;
Student s1 = Student.getStudent();
Student s2 = Student.getStudent();
System.out.println(s1 == s2);
System.out.println(s1); // null,cn.itcast_03.Student@175078b
System.out.println(s2);// null,cn.itcast_03.Student@175078b
class Student {
private Student(){}//構造方法私有化
//成員位置自己造一個對象
private static Student s = new Student();
//提供公共的外界通路方式
public static Student getStudent(){
return s;
第二個例子:建立一個懶漢式單例模式。
* 單例模式:
* 餓漢式:類一加載就建立對象
* 懶漢式:用的時候,才去建立對象
* 面試題:單例模式的思想是什麼?請寫一個代碼展現。
* 開發:餓漢式(是不會出問題的單例模式)
* 面試:懶漢式(可能會出問題的單例模式)
* A:懶加載(延遲加載)
* B:線程安全問題
* a:是否多線程環境是
* b:是否有共享資料是
* c:是否有多條語句操作共享資料 是
public class TeacherDemo {
Teacher t1 = Teacher.getTeacher();
Teacher t2 = Teacher.getTeacher();
System.out.println(t1 == t2);
System.out.println(t1); // cn.itcast_03.Teacher@175078b
class Teacher{
private Teacher(){};
private static Teacher t = null;
public synchronized static Teacher getTeacher(){
if(t == null){
t = new Teacher();
return t;