day06【面向對象基礎–上】
今日内容
- 類與對象
- 成員變量
- 成員方法
學習目标
- 初步了解面向對象的思想
- 能夠明确類與對象關系
- 能夠掌握類的定義格式
- 能夠掌握建立對象格式
- 能夠通過類通路類的靜态成員變量和靜态成員方法
- 能夠通過對象通路對象的非靜态成員變量和非靜态成員方法
- 能夠差別靜态方法和非靜态方法
- 能夠差別類變量與執行個體變量
- 能夠差別成員變量與局部變量
- 能夠了解方法的調用執行機制
- 能夠了解方法的參數傳遞機制
第五章 面向對象基礎(上)
5.1 面向對象思想概述
1、概述
Java語言是一種面向對象的程式設計語言,而面向對象思想(OOP)是一種程式設計思想,我們在面向對象思想的指引下,使用Java語言去設計、開發計算機程式。
這裡的對象泛指現實中一切事物,每種事物都具備自己的屬性和行為。面向對象思想就是在計算機程式設計過程中,參照現實中事物,将事物的屬性特征、行為特征抽象出來,描述成計算機事件的設計思想。
它差別于面向過程思想(POP),強調的是通過調用對象的行為來實作功能,而不是自己一步一步的去操作實作。
2、面向對象與面向過程的差別
面向過程:POP: Process-Oriented Programming
以函數(方法)為最小機關
資料獨立于函數之外
以過程,步驟為主,考慮怎麼做
面向對象:OOP: Object Oriented Programming
以類/對象為最小機關,類包括:資料+方法
以對象(誰)為主,考慮誰來做,誰能做
面向對象仍然包含面向過程,隻不過關注點變了,關注誰來做
程式員的角色:
面向過程:程式員是具體執行者
面向對象:程式員是指揮者
面向對象思想是一種更符合我們思考習慣的思想,它可以将複雜的事情簡單化,并将我們從執行者變成了指揮者。
例子:把大象裝進冰箱
3、面向對象的基本特征
面向對象的語言中,包含了三大基本特征,即封裝、繼承和多态。
5.2 類和對象
環顧周圍,你會發現很多對象,比如桌子,椅子,同學,老師等。桌椅屬于辦公用品,師生都是人類。那麼什麼是類呢?什麼是對象呢?
什麼是類
- 類:是一類具有相同特性的事物的抽象描述,是一組相關屬性和行為的集合。可以看成是一類事物的模闆,使用事物的屬性特征和行為特征來描述該類事物。
現實中,描述一類事物:
- 屬性:就是該事物的狀态資訊。
- 行為:就是該事物能夠做什麼。
舉例:小貓。
屬性:名字、體重、年齡、顔色。
行為:走、跑、叫。
什麼是對象
- 對象:是一類事物的具體展現。對象是類的一個執行個體(對象并不是找個女朋友),必然具備該類事物的屬性和行為。
現實中,一類事物的一個執行個體:一隻小貓 。
舉例:一隻小貓。
屬性:tom、5kg、2 years、yellow。
行為:溜牆根走、蹦跶的跑、喵喵叫。
類與對象的關系
- 類是對一類事物的描述,是抽象的。
- 對象是一類事物的執行個體,是具體的。
- 類是對象的模闆,對象是類的實體。
5.3 類的定義和對象的建立
事物與類的對比
現實世界的一類事物:
屬性:事物的狀态資訊。
行為:事物能夠做什麼。
Java中用class描述事物也是如此:
成員變量:對應事物的屬性
成員方法:對應事物的行為
類的定義格式
public class ClassName {
//成員變量
//成員方法
}
- 定義類:就是定義類的成員,包括成員變量和成員方法。
- 成員變量:和以前定義變量幾乎是一樣的。隻不過位置發生了改變。在類中,方法外。
- 成員方法:和以前寫的main方法格式類似。隻不過功能和形式更豐富了。在類中,方法外。
類的定義格式舉例:
public class Person {
//成員變量
String name;//姓名
int age;//年齡
boolean isMarried;
public void walk(){
System.out.println("人走路...");
}
public String display(){
return "名字是:" + name + ",年齡是:" + age + ",Married:" + isMarried;
}
}
[外鍊圖檔轉存失敗,源站可能有防盜鍊機制,建議将圖檔儲存下來直接上傳(img-965v2y5s-1577079439518)(imgs/1561596475149.png)]
對象的建立
建立對象:
new 類名()//也稱為匿名對象
//給建立的對象命名
//或者說,把建立的對象用一個引用資料類型的變量儲存起來
類名 對象名 = new 類名();
類似于:
System.out.println("柴老師年齡是:" + 18);//如果确定隻在這裡一次性使用,那麼可以不用變量儲存(#^.^#)
//把18用int類型的age變量儲存起來,友善後面使用
int age = 18;
System.out.println("柴老師年齡是:" + age);
System.out.println("宋老師比柴老師大10歲,年齡是:" + (age+10));
那麼,對象名中存儲的是什麼呢?答:對象位址
class Student{
}
public class TestStudent{
//Java程式的入口
public static void main(String[] args){
System.out.println(new Student());//[email protected]
Student stu = new Student();
System.out.println(stu);//[email protected]
int[] arr = new int[5];
System.out.println(arr);//[[email protected]
}
}
//Student和TestStudent沒有位置要求,誰在上面誰在下面都可以
//但是如果TestStudent類的main中使用了Student類,那麼要求編譯時,這個Student已經寫好了,不寫是不行的
//如果兩個類都在一個.java源檔案中,隻能有一個類是public的
發現學生對象和數組對象類似,直接列印對象名和數組名都是顯示“類型@對象的hashCode值",是以說類、數組都是引用資料類型,引用資料類型的變量中存儲的是對象的位址,或者說指向堆中對象的首位址。
那麼像“[email protected]”是對象的位址嗎?不是,因為Java是對程式員隐藏記憶體位址的,不暴露記憶體位址資訊,是以列印對象時不直接顯示記憶體位址,而是JVM提取了對象描述資訊給你現在,預設提取的是對象的運作時類型@代表對象唯一編碼的hashCode值。
5.4 成員變量
1、成員變量的分類
執行個體變量:沒有static修飾,也叫對象屬性,屬于某個對象的,通過對象來使用
類變量:有static修飾,也叫類變量,屬于整個類的,不是屬于某個執行個體
2、如何聲明成員變量?
【修飾符】 class 類名{
【修飾符】 資料類型 屬性名; //屬性有預設值
【修飾符】 資料類型 屬性名 = 值; //屬性有初始值
}
說明:屬性的類型可以是Java的任意類型,包括基本資料類型、引用資料類型(類、接口、數組等)
例如:聲明一個中國人的類
class Chinese{
static String country;
String name;
char gender = '男';//顯式指派
}
3、如何在類外面通路成員變量?
(1)類變量
類名.靜态成員變量 //推薦
對象名.靜态成員變量 //不推薦
(2)執行個體變量
例如:
public class TestChinese {
public static void main(String[] args) {
//類名.靜态成員變量
System.out.println(Chinese.country);
//錯誤,非靜态成員變量必須通過對象.進行通路
// System.out.println(Chinese.name);
Chinese c1 = new Chinese();
//對象名.非靜态成員變量
System.out.println(c1.name);
//靜态的成員變量也可以通過對象.進行通路
//對象名.非靜态成員變量
System.out.println(c1.country);
System.out.println(c1.gender);
}
}
class Chinese{
static String country;
String name;
char gender = '男';
}
4、成員變量的特點
(1)成員變量有預設值
基本類型 | 整數(byte,short,int,long) | |
---|---|---|
浮點數(float,double) | 0.0 | |
字元(char) | ‘\u0000’ | |
布爾(boolean) | false | |
資料類型 | 預設值 | |
引用類型 | 數組,類,接口 | null |
(2)類變量的值是所有對象共享的,而執行個體變量的值是每個對象獨立的
public class TestChinese {
public static void main(String[] args) {
Chinese c1 = new Chinese();
Chinese c2 = new Chinese();
c1.name = "張三";
c2.name = "李四";
c2.gender = '女';
// c1.country = "中國";
Chinese.country = "中國";//推薦
System.out.println("c1.country = " + c1.country + ",c1.name = " + c1.name + ",c1.gender = " + c1.gender);
System.out.println("c2.country = " + c2.country + ",c2.name = " + c2.name + ",c2.gender = " + c2.gender);
}
}
class Chinese{
static String country;
String name;
char gender = '男';
}
5、成員變量的記憶體圖
記憶體是計算機中重要的部件之一,它是與CPU進行溝通的橋梁。其作用是用于暫時存放CPU中的運算資料,以及與硬碟等外部存儲器交換的資料。隻要計算機在運作中,CPU就會把需要運算的資料調到記憶體中進行運算,當運算完成後CPU再将結果傳送出來。我們編寫的程式是存放在硬碟中的,在硬碟中的程式是不會運作的,必須放進記憶體中才能運作,運作完畢後會清空記憶體。Java虛拟機要運作程式,必須要對記憶體進行空間的配置設定和管理,每一片區域都有特定的處理資料方式和記憶體管理方式。
區域名稱 | 作用 |
---|---|
程式計數器 | 程式計數器是CPU中的寄存器,它包含每一個線程下一條要執行的指令的位址 |
本地方法棧 | 當程式中調用了native的本地方法時,本地方法執行期間的記憶體區域 |
方法區 | 存儲已被虛拟機加載的類資訊、常量、靜态變量、即時編譯器編譯後的代碼等資料。 |
堆記憶體 | 存儲對象(包括數組對象),new來建立的,都存儲在堆記憶體。 |
虛拟機棧 | 用于存儲正在執行的每個Java方法的局部變量表等。局部變量表存放了編譯期可知長度的各種基本資料類型、對象引用,方法執行完,自動釋放。 |
class Test08FieldSave{
public static void main(String[] args){
Chinese c1 = new Chinese();
c1.name = "張三";
System.out.println(c1.country);//靜态變量,也可以使用"對象名."進行通路
System.out.println(c1.name);//非靜态的執行個體變量通過"對象名."進行通路
Chinese c2 = new Chinese();
c2.name = "李四";
System.out.println(c2.country);
System.out.println(c2.name);
System.out.println("--------------------------------------");
//其中一個對象将靜态變量的值修改了,其他對象都會改變
//因為靜态變量隻存一份
c1.country = "中華人民共和國";
System.out.println(c1.country);
System.out.println(c2.country);
System.out.println(Chinese.country);
//其中一個對象将非靜态執行個體變量修改了,其他對象不受影響
c1.name = "張三豐";
System.out.println(c1.name);
System.out.println(c2.name);
}
}
class Chinese{
static String country = "中國";//靜态變量,所有中國人的國家的名稱是一樣,隻需要存儲一份
String name;//執行個體變量,每一個中國人的姓名是獨立,每一個對象單獨存儲
}
class MyDate{
int year;
int month;
int day;
}
class Employee{
String name;
MyDate birthday;
}
class Test09FieldExer3{
public static void main(String[] args){
//建立兩個員工對象
Employee e1 = new Employee();
Employee e2 = new Employee();
//為兩個員工對象的成員變量指派
e1.name = "張三";
e1.birthday = new MyDate();
e2.name = "李四";
e2.birthday = new MyDate();
e1.birthday.year = 2000;
e1.birthday.month = 1;
e1.birthday.day = 1;
e2.birthday.year = 2000;
e2.birthday.month = 3;
e2.birthday.day = 8;
System.out.println("第一個員工,姓名:" + e1.name +",生日:" + e1.birthday.year + "年" + e1.birthday.month + "月" + e1.birthday.day + "日");
System.out.println("第二個員工,姓名:" + e2.name +",生日:" + e2.birthday.year + "年" + e2.birthday.month + "月" + e2.birthday.day + "日");
}
}
6、成員變量練習題
(1)聲明一個圓的圖形類,有屬性:半徑
在測試類的main中,建立圓的2個對象,為半徑屬性指派,并顯示兩個圓的半徑值和面積值
提示:圓周率為Math.PI
(2)聲明一個銀行賬戶類,有屬性:利率、賬号、餘額
在測試類的main中,建立賬戶類的兩個對象,其中所有賬戶的利率是相同的,都是0.035,而賬号和餘額是不同的,并列印顯示
(3)聲明一個MyDate類型,有屬性:年,月,日
聲明另一個Employee類型,有屬性:姓名(String類型),生日(MyDate類型)
在測試類中的main中,建立兩個員工對象,并為他們的姓名和生日指派,并顯示
5.5 成員方法
成員變量是用來存儲對象的資料資訊的,那麼如何表示對象的行為功能呢?就要通過方法來實作
5.5.1 方法的概念
方法也叫函數,是一個獨立功能的定義,是一個類中最基本的功能單元。
把一個功能封裝為方法的目的是,可以實作代碼重用,進而簡少代碼量。
5.5.2 方法的原則
方法的使用原則:
(1)必須先聲明後使用
類,變量,方法等都要先聲明後使用
(2)不調用不執行,調用一次執行一次。
5.5.3 成員方法的分類
成員方法分為兩類:
- 執行個體方法:沒有static修飾的方法,必須通過執行個體對象來調用。
- 靜态方法:有static修飾的方法,也叫類方法,可以由類名來調用。
5.5.4 如何聲明方法
1、方法聲明的位置必須在類中方法外
2、文法格式
【修飾符】 傳回值類型 方法名(【參數清單:參數類型1 參數名1,參數類型2 參數名, ...... 】){
方法體;
【return 傳回值;】
}
- 修飾符: 修飾符後面一一介紹,例如:public,static等都是修飾符
- 傳回值類型: 表示方法運作的結果的資料類型,方法執行後将結果傳回到調用者
- 基本資料類型
- 引用資料類型
- 無傳回值類型:void
- 方法名:給方法起一個名字,見名知意,能準确代表該方法功能的名字
- 參數清單:方法内部需要用到其他方法中的資料,需要通過參數傳遞的形式将資料傳遞過來,可以是基本資料類型、引用資料類型、也可以沒有參數,什麼都不寫
- 方法體:特定功能代碼
- return:結束方法,并将方法的結果傳回去,
- 如果傳回值類型不是void,方法體中必須保證一定有return 傳回值;語句,并且要求該傳回值結果的類型與聲明的傳回值類型一緻或相容。
- 如果傳回值類型為void時,return 後面不用跟傳回值,甚至也可以沒有return語句。
- return語句後面就不能再寫其他代碼了,否則會報錯:Unreachable code
聲明位置示例:
類{
方法1(){
}
方法2(){
}
}
錯誤示例:
類{
方法1(){
方法2(){ //位置錯誤
}
}
}
示例一:
聲明一個圓的圖形類:
屬性(成員變量):半徑,
成員方法:求面積的方法,傳回圓對象資訊的方法
在測試類的main中,建立圓的2個對象,為半徑屬性指派,調用兩個方法進行測試
提示:圓周率為Math.PI
class Circle{
double radius;
double area() {
return Math.PI * radius * radius;
}
}
Circle不同的對象,半徑值不同,那麼面積也不同,是以這裡area()是非靜态的
示例二:
聲明一個計算工具類CountTools:
方法1:求兩個整數的最大值
class CountTools{
static int max(int a, int b) {
return a > b ? a : b;
}
}
CountTools隻是一個工具類,求兩個整數最大值的功能,和CountTools對象無關,是以這裡max方法聲明為靜态的更好,當然也可以聲明為非靜态的,就是調用的時候需要建立CountTools對象而已。
5.5.5 如何在其他類中調用方法
(1)執行個體方法
示例代碼:
public class TestCircle {
public static void main(String[] args) {
Circle c1 = new Circle();
c1.radius = 1.2;
System.out.println("c1的面積:" + c1.area());
//非靜态方法隻能通過"對象."進行通路
// System.out.println("c1的面積:" + Circle.area());
Circle c2 = new Circle();
c2.radius = 2.5;
System.out.println("c2的面積:" + c2.area());
}
}
class Circle{
double radius;
public double area() {
return Math.PI * radius * radius;
}
}
(2)類方法
類名.類方法(【實參清單】) //推薦
對象名.類方法(【實參清單】) //不推薦
示例:
public class TestCount {
public static void main(String[] args) {
System.out.println(CountTools.max(4, 1));
//靜态方法也可以通過“對象.”通路,就是麻煩點
CountTools c = new CountTools();
System.out.println(c.max(2, 5));
}
}
class CountTools{
static int max(int a, int b) {
return a > b ? a : b;
}
}
(3)總結
- 形參:在定義方法時方法名後面括号中聲明的變量稱為形式參數(簡稱形參)即形參出現在方法定義時。
- 實參:調用方法時方法名後面括号中的使用的值/變量/表達式稱為實際參數(簡稱實參)即實參出現在方法調用時。
總結:
(1)調用時,需要傳“實參”,實參的個數、類型、順序順序要與形參清單一一對應
如果方法沒有形參,就不需要也不能傳實參。
(2)調用時,如果方法有傳回值,可以接受或處理傳回值結果,當然也可以不接收,那麼此時傳回值就丢失了。
如果方法的傳回值類型是void,不需要也不能接收和處理傳回值結果。
5.5.6 在本類中通路本類的成員變量和成員方法
直接用,不需要加“對象名.“和"類名.”
唯一例外:靜态方法中不能直接通路本類的非靜态的成員變量和成員方法
class Circle{
double radius;
//寫一個方法,可以傳回“圓對象”的詳細資訊
String getDetailInfo(){
return "半徑:" + radius + ",面積:" + area() +",周長:" + perimeter();
}
//寫一個方法,可以傳回“圓對象”的面積
double area(){
return Math.PI*radius*radius;
}
//寫一個方法,可以傳回“圓對象”的周長
double perimeter(){
return 2*Math.PI*radius;
}
}
class Test{
static void test(){
System.out.println("");
}
void method(){
test();
}
public static void main(String[] args){
method();//錯誤
test();//正确
}
}
5.5.7 方法的聲明與調用練習
1、聲明數學工具類MathTools
(1)靜态方法1:可以比較兩個整數是否相同
(2)靜态方法2:可以判斷某個數是否是素數
(3)靜态方法3:可以傳回某個整數所有的約數(約數:從1到這個數之間所有能把它整除的數)
在Test測試類的main中調用測試
2、聲明數組工具類ArraysTools
(1)靜态方法1:可以實作給任意整型數組實作從小到大排序
(2)靜态方法2:可以周遊任意整型數組,傳回結果效果:[元素1,元素2,元素3。。。]
3、聲明矩形類
(1)包含屬性:長、寬
(2)包含3個方法:
求面積、
求周長、
傳回矩形對象的資訊:長:xx,寬:xx,面積:xx,周長:xx
4、聲明一個圓類,有半徑radius成員變量
聲明一個圖形工具類GraphicTools,包含一個靜态方法可以傳回兩個圓中面積大的那一個圓的方法
在測試類中測試
5.5.8 方法調用記憶體分析
方法不調用不執行,調用一次執行一次,每次調用會在棧中有一個入棧動作,即給目前方法開辟一塊獨立的記憶體區域,用于存儲目前方法的局部變量的值,當方法執行結束後,會釋放該記憶體,稱為出棧,如果方法有傳回值,就會把結果傳回調用處,如果沒有傳回值,就直接結束,回到調用處繼續執行下一條指令。
棧結構:先進後出,後進先出。
示例一:
public class TestCount {
public static void main(String[] args) {
int a = 4;
int b = 2;
int m = CountTools.max(a, b));
}
}
class CountTools{
static int max(int a, int b) {
return a > b ? a : b;
}
}
示例二:
public class TestCircle {
public static void main(String[] args) {
Circle c1 = new Circle();
c1.radius = 1.2;
int area1 = c1.area();
Circle c2 = new Circle();
c2.radius = 2.5;
int area2 = c2.area();
}
}
class Circle{
double radius;
public double area() {
return Math.PI * radius * radius;
}
}
示例三:
public class Test {
public static void main(String[] args) {
int[] arr = {2,4,1,5,3};
ArrayUtil.sort(arr);
for (int i = 0; i < arr.length; i++) {
System.out.println(arr[i]);
}
}
}
class ArrayUtil{
public static void sort(int[] arr){
for (int i = 1; i < arr.length; i++) {
for (int j = 0; j < arr.length - i; j++) {
if(arr[j] > arr[j+1]){
int temp = arr[j];
arr[j] = arr[j+1];
arr[j+1] = temp;
}
}
}
}
}
5.5.9 方法的參數傳遞機制
- 方法的參數傳遞機制:實參給形參指派
- 方法的形參是基本資料類型時,形參值的改變不會影響實參;
- 方法的形參是引用資料類型時,形參位址值的改變不會影響實參,但是形參位址值裡面的資料的改變會影響實參,例如,修改數組元素的值,或修改對象的屬性值。
- 注意:String、Integer等特殊類型容易錯
示例代碼1:
class Test{
public static void swap(int a, int b){
int temp = a;
a = b;
b = temp;
}
public static void main(String[] args){
int x = 1;
int y = 2;
swap(x,y);//調用完之後,x與y的值不變
}
}
示例代碼2:
class Test{
public static void change(MyData my){
my.num *= 2;
}
public static void main(String[] args){
MyData m = new MyData();
m.num = 1;
change(m);//調用完之後,m對象的num屬性值就變為2
}
}
class MyData{
int num;
}
示例代碼3:
public class Test {
public static void main(String[] args) {
int[] arr = {2,4,1,5,3};
ArrayUtil.sort(arr);
for (int i = 0; i < arr.length; i++) {
System.out.println(arr[i]);
}
}
}
class ArrayUtil{
public static void sort(int[] arr){
for (int i = 1; i < arr.length; i++) {
for (int j = 0; j < arr.length - i; j++) {
if(arr[j] > arr[j+1]){
int temp = arr[j];
arr[j] = arr[j+1];
arr[j+1] = temp;
}
}
}
}
}
陷阱1:
/*
陷阱1:在方法中,形參 = 新new對象,那麼就和實參無關了
*/
class Test{
public static void change(MyData my){
my = new MyData();//形參指向了新對象
my.num *= 2;
}
public static void main(String[] args){
MyData m = new MyData();
m.num = 1;
change(m);//調用完之後,m對象的num屬性值仍然為1
}
}
class MyData{
int num;
}
陷阱2:見字元串和包裝類部分
public class Test {
public static void main(String[] args) {
StringUtil util = new StringUtil();
String str = "尚矽谷";
util.change(str);
System.out.println(str);
}
}
class StringUtil{
public void change(String str){
str += "你好";//String對象不可變,一旦修改就會産生新對象
}
}
5.5.10 成員變量與局部變量的差別
(1)聲明的位置不同
成員變量:類中方法外
局部變量:方法中
(2)初始值不同
成員變量:有預設值
局部變量:必須手動初始化
(3)記憶體存儲位置不同
成員變量:
類變量:方法區
執行個體變量:堆
局部變量:
棧
(4)生命周期
成員變量:
類變量:和類的生命周期相同,該類所有對象共享
執行個體變量:每一個對象的執行個體變量的生命周期是獨立的,随着對象的建立而建立,随着對象的回收而消失
局部變量:随着方法被調用執行在棧中配置設定,方法調用結束記憶體就釋放,并且還有作用域問題。
(5)修飾符
成員變量:有很多修飾符,例如:public,private,static等
向了新對象
my.num *= 2;
}
public static void main(String[] args){
MyData m = new MyData();
m.num = 1;
change(m);//調用完之後,m對象的num屬性值仍然為1
}
}
class MyData{
int num;
}
陷阱2:見字元串和包裝類部分
```java
public class Test {
public static void main(String[] args) {
StringUtil util = new StringUtil();
String str = "尚矽谷";
util.change(str);
System.out.println(str);
}
}
class StringUtil{
public void change(String str){
str += "你好";//String對象不可變,一旦修改就會産生新對象
}
}
5.5.10 成員變量與局部變量的差別
(1)聲明的位置不同
成員變量:類中方法外
局部變量:方法中
(2)初始值不同
成員變量:有預設值
局部變量:必須手動初始化
(3)記憶體存儲位置不同
成員變量:
類變量:方法區
執行個體變量:堆
局部變量:
棧
(4)生命周期
成員變量:
類變量:和類的生命周期相同,該類所有對象共享
執行個體變量:每一個對象的執行個體變量的生命周期是獨立的,随着對象的建立而建立,随着對象的回收而消失
局部變量:随着方法被調用執行在棧中配置設定,方法調用結束記憶體就釋放,并且還有作用域問題。
(5)修飾符
成員變量:有很多修飾符,例如:public,private,static等
局部變量:不能有修飾符