java面向?qū)ο蠡A(chǔ)(上)
面向?qū)ο蠡A(chǔ)(上)
學(xué)習(xí)目標(biāo)
- 理解方法的概念和特點(diǎn)
- 掌握靜態(tài)方法的聲明和調(diào)用
- 知道形參與實(shí)參的關(guān)系
- 理解方法參數(shù)傳遞機(jī)制的原理
- 理解方法調(diào)用的入棧與出棧過(guò)程
- 了解可變參數(shù)的聲明和使用
- 掌握方法重載的概念和要求
- 了解方法遞歸調(diào)用的概念
- 理解類與對(duì)象的關(guān)系
- 掌握用class聲明類的語(yǔ)法格式
- 掌握用new創(chuàng)建對(duì)象的語(yǔ)法格式
- 掌握靜態(tài)變量的聲明和使用
- 掌握實(shí)例方法的聲明和調(diào)用
- 掌握實(shí)例變量的聲明和使用
- 掌握包的作用和創(chuàng)建
- 知道靜態(tài)變量、實(shí)例變量、局部變量的區(qū)別
5.1 方法的基礎(chǔ)知識(shí)(重點(diǎn)掌握)
5.1.1 方法的概念
方法也叫函數(shù),是一組代碼語(yǔ)句的封裝,從而實(shí)現(xiàn)代碼重用,從而減少冗余代碼,通常它是一個(gè)獨(dú)立功能的定義,方法是一個(gè)類中最基本的功能單元。
Math.random()的random()方法
Math.sqrt(x)的sqrt(x)方法
System.out.println(x)的println(x)方法
Scanner input = new Scanner(System.in);
input.nextInt()的nextInt()方法
5.1.2 方法的特點(diǎn)
(1)必須先聲明后使用
類,變量,方法等都要先聲明后使用
- (2)不調(diào)用不執(zhí)行,調(diào)用一次執(zhí)行一次。
5.1.3 如何聲明靜態(tài)方法
1、聲明方法的位置
聲明方法的位置==必須在類中方法外==,即不能在一個(gè)方法中直接定義另一個(gè)方法。
正確示例:
類{
方法1(){
}
方法2(){
}
}
錯(cuò)誤示例:
類{
方法1(){
方法2(){ //位置錯(cuò)誤
}
}
}
2、聲明方法的語(yǔ)法格式
【修飾符】 返回值類型 方法名(【形參列表 】)【throws 異常列表】{ 方法體的功能代碼 }
一個(gè)完整的方法 = 方法頭 + 方法體。
- 方法頭就是 【修飾符】 返回值類型 方法名(【形參列表 】)【throws 異常列表】,也稱為方法簽名,通常調(diào)用方法時(shí)只需要關(guān)注方法頭就可以,從方法頭可以看出這個(gè)方法的功能和調(diào)用格式。方法頭可能包含5個(gè)部分,但是有些部分是可能缺省的。
- 方法體就是方法被調(diào)用后要指定的代碼,也是完成方法功能的具體實(shí)現(xiàn)代碼,對(duì)于調(diào)用者來(lái)說(shuō),不了解方法體如何實(shí)現(xiàn)的,并不影響方法的使用。
3、方法每一個(gè)部分的含義
- (1)**方法名(必選)**:給方法起一個(gè)名字,見(jiàn)名知意,能準(zhǔn)確代表該方法功能的名字
(2)【**修飾符】(可選)**:會(huì)影響方法的調(diào)用方式,以及方法的可見(jiàn)性范圍等
修飾符:可選的。方法的修飾符也有很多,例如:public、protected、private、static、abstract、native、final、synchronized等,后面會(huì)一一學(xué)習(xí)。其中根據(jù)是否有static,可以將方法分為靜態(tài)方法和非靜態(tài)方法。其中靜態(tài)方法又稱為類方法,非靜態(tài)方法又稱為實(shí)例方法。==接下來(lái)咱們先學(xué)習(xí)靜態(tài)方法==。
- (3)**【throws 異常列表】(可選)**:這個(gè)部分在異常章節(jié)再講
(4)**(【參數(shù)列表】)(()必選,參數(shù)列表可選)**:表示完成方法體功能時(shí)需要“調(diào)用者”提供的數(shù)據(jù)列表
- 無(wú)論是否有參數(shù),()不能省略
- 如果有參數(shù),每一個(gè)參數(shù)都要指定數(shù)據(jù)類型和參數(shù)名,多個(gè)參數(shù)之間使用**逗號(hào)**分隔,例如:
- 一個(gè)參數(shù): (數(shù)據(jù)類型 參數(shù)名)
- 二個(gè)參數(shù): (數(shù)據(jù)類型1 參數(shù)1, 數(shù)據(jù)類型2 參數(shù)2)
- 參數(shù)的類型可以是基本數(shù)據(jù)類型、引用數(shù)據(jù)類型
(5)**返回值類型(必選)**: 表示方法運(yùn)行的結(jié)果的數(shù)據(jù)類型,方法執(zhí)行后將結(jié)果返回到調(diào)用者
- 基本數(shù)據(jù)類型
- 引用數(shù)據(jù)類型
- 無(wú)返回值類型:void
- (6)**{方法體}(必選)**:方法體必須有{}括起來(lái),在{}中編寫(xiě)完成方法功能的代碼,具有方法體的方法才能被調(diào)用執(zhí)行。
關(guān)于方法體中return語(yǔ)句的說(shuō)明:
- - return語(yǔ)句的作用是結(jié)束方法的執(zhí)行,并將方法的結(jié)果返回去
- - 如果返回值類型不是void,方法體中必須保證一定有 return 返回值; 語(yǔ)句,并且要求該返回值結(jié)果的類型與聲明的返回值類型一致或兼容。
- - 如果返回值類型為void時(shí),方法體中可以沒(méi)有return語(yǔ)句,如果要用return語(yǔ)句提前結(jié)束方法的執(zhí)行,那么return后面不能跟返回值,直接寫(xiě)return ; 就可以。
- - return語(yǔ)句后面就不能再寫(xiě)其他代碼了,否則會(huì)報(bào)錯(cuò):Unreachable code
package com.atguigu.test04.method;
/**
* 方法定義案例演示
*/
public class MethodDefineDemo {
/**
* 無(wú)參無(wú)返回值方法的演示
*/
public static void sayHello(){
System.out.println("hello");
}
/**
* 有參無(wú)返回值方法的演示
* @param length int 第一個(gè)參數(shù),表示矩形的長(zhǎng)
* @param width int 第二個(gè)參數(shù),表示矩形的寬
* @param sign char 第三個(gè)參數(shù),表示填充矩形圖形的符號(hào)
*/
public static void printRectangle(int length, int width, char sign){
for (int i = 1; i <= length ; i++) {
for(int j=1; j <= width; j++){
System.out.print(sign);
}
System.out.println();
}
}
/**
* 無(wú)參有返回值方法的演示
* @return
*/
public static int getIntBetweenOneToHundred(){
return (int)(Math.random()*100+1);
}
/**
* 有參有返回值方法的演示
* @param a int 第一個(gè)參數(shù),要比較大小的整數(shù)之一
* @param b int 第二個(gè)參數(shù),要比較大小的整數(shù)之二
* @return int 比較大小的兩個(gè)整數(shù)中較大者的值
*/
public static int max(int a, int b){
return a > b? a : b;
}
}
5.1.4 如何調(diào)用靜態(tài)方法
1、本類中
【修飾符】 class 類名{
【public】 static 返回值類型 靜態(tài)方法1(【形參列表】){
....
}
【public】 static 返回值類型 靜態(tài)方法2(【形參列表】){
靜態(tài)方法1(【實(shí)參列表】);
}
}
2、其他類中
【修飾符】 class A類名{
【public】 static 返回值類型 靜態(tài)方法1(【形參列表】){
B類名.靜態(tài)方法2(【實(shí)參列表】);
}
}
【修飾符】 class B類名{
【public】 static 返回值類型 靜態(tài)方法2(【形參列表】){
....
}
}
例如:
package com.atguigu.test04.method;
/**
* 方法調(diào)用案例演示
*/
public class MethodInvokeDemo {
public static void main(String[] args) {
System.out.println("-----------------------方法調(diào)用演示-------------------------");
//調(diào)用MethodDefineDemo類中無(wú)參無(wú)返回值的方法sayHello
MethodDefineDemo.sayHello();
MethodDefineDemo.sayHello();
MethodDefineDemo.sayHello();
//調(diào)用一次,執(zhí)行一次,不調(diào)用不執(zhí)行
System.out.println("------------------------------------------------");
//調(diào)用MethodDefineDemo類中有參無(wú)返回值的方法printRectangle
MethodDefineDemo.printRectangle(5,10,'@');
System.out.println("------------------------------------------------");
//調(diào)用MethodDefineDemo類中無(wú)參有返回值的方法getIntBetweenOneToHundred();
MethodDefineDemo.getIntBetweenOneToHundred();//語(yǔ)法沒(méi)問(wèn)題,就是結(jié)果丟失
int num = MethodDefineDemo.getIntBetweenOneToHundred();
System.out.println("num = " + num);
System.out.println(MethodDefineDemo.getIntBetweenOneToHundred());
//上面的代碼調(diào)用了getIntBetweenOneToHundred三次,這個(gè)方法執(zhí)行了三次
System.out.println("------------------------------------------------");
//調(diào)用MethodDefineDemo類中有參有返回值的方法max
MethodDefineDemo.max(3,6);//語(yǔ)法沒(méi)問(wèn)題,就是結(jié)果丟失
int bigger = MethodDefineDemo.max(5,6);
System.out.println("bigger = " + bigger);
System.out.println("8,3中較大者是:" + MethodDefineDemo.max(8,9));
}
}
5.1.5 方法調(diào)用內(nèi)存分析
方法不調(diào)用不執(zhí)行,調(diào)用一次執(zhí)行一次,每次調(diào)用會(huì)在棧中有一個(gè)入棧動(dòng)作,即給當(dāng)前方法開(kāi)辟一塊獨(dú)立的內(nèi)存區(qū)域,用于存儲(chǔ)當(dāng)前方法的局部變量的值,當(dāng)方法執(zhí)行結(jié)束后,會(huì)釋放該內(nèi)存,稱為出棧,如果方法有返回值,就會(huì)把結(jié)果返回調(diào)用處,如果沒(méi)有返回值,就直接結(jié)束,回到調(diào)用處繼續(xù)執(zhí)行下一條指令。
棧結(jié)構(gòu):先進(jìn)后出,后進(jìn)先出。
5.1.6 方法的參數(shù)傳遞機(jī)制
方法無(wú)論是否有參數(shù),聲明方法和調(diào)用方法是==()都不能丟失==
* 形參(formal parameter):在定義方法時(shí)方法名后面括號(hào)中聲明的變量稱為形式參數(shù)(簡(jiǎn)稱形參)即形參出現(xiàn)在方法定義時(shí)。
* 實(shí)參(actual parameter):調(diào)用方法時(shí)方法名后面括號(hào)中的使用的值/變量/表達(dá)式稱為實(shí)際參數(shù)(簡(jiǎn)稱實(shí)參)即實(shí)參出現(xiàn)在方法調(diào)用時(shí)。
* 實(shí)參的作用就是給形參賦值。調(diào)用時(shí),實(shí)參的個(gè)數(shù)、類型、順序順序要與形參列表一一對(duì)應(yīng)。如果方法沒(méi)有形參,就不需要也不能傳實(shí)參。
方法的參數(shù)傳遞機(jī)制:實(shí)參給形參賦值,那么反過(guò)來(lái)形參會(huì)影響實(shí)參嗎?
* 方法的形參是基本數(shù)據(jù)類型時(shí),形參值的改變不會(huì)影響實(shí)參;
* 方法的形參是引用數(shù)據(jù)類型時(shí),形參地址值的改變不會(huì)影響實(shí)參,但是形參地址值里面的數(shù)據(jù)的改變會(huì)影響實(shí)參,例如,修改數(shù)組元素的值,或修改對(duì)象的屬性值。
* 注意:String、Integer等特殊類型容易錯(cuò)
1、形參是基本數(shù)據(jù)類型
案例:編寫(xiě)方法,交換兩個(gè)整型變量的值
public class PrimitiveTypeParam {
public static void swap(int a, int b){//交換兩個(gè)形參的值
int temp = a;
a = b;
b = temp;
}
public static void main(String[] args) {
int x = 1;
int y = 2;
System.out.println("交換之前:x = " + x +",y = " + y);//1,2
swap(x,y);//實(shí)參x,y是基本數(shù)據(jù)類型,給形參的是數(shù)據(jù)的“副本”,調(diào)用完之后,x與y的值不變
System.out.println("交換之后:x = " + x +",y = " + y);//1,2
}
}
2、形參是引用數(shù)據(jù)類型
public class ArrayTypeParam {
public static void sort(int[] arr){//這里對(duì)arr數(shù)組進(jìn)行排序,就相當(dāng)于對(duì)nums數(shù)組進(jìn)行排序
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;
}
}
}
}
public static void iterate(int[] arr){//輸出數(shù)組的元素,元素之間使用空格分隔,元素打印完之后換行
for (int i = 0; i < arr.length; i++) {
System.out.print(arr[i]+" ");
}
System.out.println();
}
public static void main(String[] args) {
int[] nums = {4,3,1,6,7};
System.out.println("排序之前:");
iterate(nums);//實(shí)參nums把數(shù)組的首地址給形參arr,這個(gè)調(diào)用相當(dāng)于輸出nums數(shù)組的元素
sort(nums);//對(duì)nums數(shù)組進(jìn)行排序
System.out.println("排序之后:");
iterate(nums);//輸出nums數(shù)組的元素
//上面的代碼,從頭到尾,堆中只有一個(gè)數(shù)組,沒(méi)有產(chǎn)生新數(shù)組,無(wú)論是排序還是遍歷輸出都是同一個(gè)數(shù)組
}
}
5.1.7 返回值問(wèn)題
一個(gè)方法最多只能有一個(gè)返回值。如果沒(méi)有值需要返回,則用void表示。如果要返回多個(gè)值,可以用數(shù)組(或其他容器,后面再講)將多個(gè)結(jié)果裝起來(lái),然后返回?cái)?shù)組。
1、void類型
如果被調(diào)用方法的返回值類型是void,表示沒(méi)有返回值。那么調(diào)用時(shí)不需要也不能接收和處理返回值結(jié)果,即方法調(diào)用表達(dá)式==只能==直接加;成為一個(gè)獨(dú)立語(yǔ)句。
2、非void類型
如果被調(diào)用方法的返回值類型不是void,表示有返回值。那么調(diào)用時(shí)可以選擇接收也可以選擇不接收返回值結(jié)果。
- - 被調(diào)用方法有結(jié)果返回,但是調(diào)用時(shí)沒(méi)有接收和使用返回值,返回值就會(huì)丟失。即方法調(diào)用表達(dá)式直接加;成為一個(gè)獨(dú)立的語(yǔ)句。
- 被調(diào)用方法有結(jié)果返回,調(diào)用時(shí)可以接收和使用返回的值。返回的結(jié)果怎么用,取決于返回值的類型是什么。
- 方法調(diào)用表達(dá)式的結(jié)果可以作為賦值表達(dá)式的值
- 方法調(diào)用表達(dá)式的結(jié)果可以作為計(jì)算表達(dá)式的一個(gè)操作數(shù)
- 方法調(diào)用表達(dá)式的結(jié)果可以作為另一次方法調(diào)用的實(shí)參
- ......
package com.atguigu.test04.method;
public class MethodReturnValue {
public static void main(String[] args) {
//無(wú)返回值的都只能單獨(dú)加;成一個(gè)獨(dú)立語(yǔ)句
//調(diào)用MethodDefineDemo類中無(wú)參無(wú)返回值的方法sayHello
MethodDefineDemo.sayHello();
//調(diào)用MethodDefineDemo類中有參無(wú)返回值的方法printRectangle
MethodDefineDemo.printRectangle(5,10,'@');
//有返回值的
//(1)方法調(diào)用表達(dá)式可以作為賦值表達(dá)式的值
int bigger = MethodDefineDemo.max(7,3);
System.out.println("bigger = " + bigger);
//(2)方法調(diào)用表達(dá)式可以作為計(jì)算表達(dá)式的一個(gè)操作數(shù)
//隨機(jī)產(chǎn)生兩個(gè)[1,10]之間的整數(shù),并求和
int sum = MethodDefineDemo.getIntBetweenOneToHundred() + MethodDefineDemo.getIntBetweenOneToHundred();
System.out.println("sum = " + sum);
//(3)方法調(diào)用表達(dá)式可以作為另一次方法調(diào)用的實(shí)參
int x = 4;
int y = 5;
int z = 2;
int biggest = MethodDefineDemo.max(MethodDefineDemo.max(x,y),z);
System.out.println("biggest = " + biggest);
//(4)方法調(diào)用表達(dá)式直接加;成為一個(gè)獨(dú)立的語(yǔ)句,這種情況,返回值丟失
MethodDefineDemo.getIntBetweenOneToHundred();
}
}
(1)基本數(shù)據(jù)類型
返回基本數(shù)據(jù)類型的數(shù)據(jù)值;
package com.atguigu.method;
public class TestMethodReturnValue1 {
public static void main(String[] args) {
int sum = add(5, 3);
System.out.println("sum = " + sum);
}
public static int add(int a, int b){
return a + b;
}
}
(2)引用數(shù)據(jù)類型
返回對(duì)象,本質(zhì)上返回的是對(duì)象的首地址。
package com.atguigu.method;
public class TestMethodReturnValue2 {
public static void main(String[] args) {
int[] numbers = getNumbersInScope(5, 10, 100);
for (int i = 0; i < numbers.length; i++) {
System.out.println(numbers[i]+" ");
}
}
public static int[] getNumbersInScope(int length,int start, int end){
int[] arr = new int[length];
for (int i = 0; i < arr.length; i++) {
arr[i] = (int)(Math.random()*(end-start)+start);
}
return arr;
}
}
5.2 命令行參數(shù)(可選拓展)
給main方法傳遞的實(shí)參稱為命令行參數(shù)。
public class TestCommandParam{
//形參:String[] args
public static void main(String[] args){
System.out.println(args);
System.out.println(args.length);
for(int i=0; i
命令行:
java TestCommandParam
java TestCommandParam 1 2 3
java TestCommandParam hello atguigu
IDEA工具:
- (1)配置運(yùn)行參數(shù)


- (2)運(yùn)行程序

5.3 可變參數(shù)(簡(jiǎn)單了解)
在**JDK1.5**之后,當(dāng)定義一個(gè)方法時(shí),形參的類型可以確定,但是形參的個(gè)數(shù)不確定,那么可以考慮使用可變參數(shù)。
1、可變參數(shù)的格式:
【修飾符】 返回值類型 方法名(【非可變參數(shù)部分的形參列表,】參數(shù)類型... 形參名){ }
2、可變參數(shù)的聲明:
- (1)一個(gè)方法最多只能有一個(gè)可變參數(shù)
- (2)如果一個(gè)方法包含可變參數(shù),那么可變參數(shù)必須是形參列表的最后一個(gè)
3、可變參數(shù)的使用:
- (1)在聲明它的方法中,可變參數(shù)當(dāng)成數(shù)組使用
(2)調(diào)用帶可變參數(shù)的方法時(shí):
- 非可變參數(shù)部分必須傳入對(duì)應(yīng)類型和個(gè)數(shù)的實(shí)參;
- 可變參數(shù)部分按照可變參數(shù)的規(guī)則傳入0~n個(gè)對(duì)應(yīng)類型的實(shí)參或傳入1個(gè)對(duì)應(yīng)類型的數(shù)組實(shí)參;
4、對(duì)比可變參數(shù)與數(shù)組類型的參數(shù):
其實(shí)”數(shù)據(jù)類型...“約等于”數(shù)據(jù)類型[]“ ,只是”數(shù)據(jù)類型[]“ 這種定義,在調(diào)用時(shí)必須傳遞數(shù)組,而”數(shù)據(jù)類型...“更靈活,既可以傳遞數(shù)組,又可以直接傳遞數(shù)組的元素。
案例:求n個(gè)整數(shù)的和
public class VarParamTest {
public static void main(String[] args) {
int[] arr = {1,2,3};
System.out.println(add(arr));
System.out.println(add(new int[]{1,2,3,4,5}));
System.out.println(add(new int[]{1}));
// System.out.println(add());//報(bào)錯(cuò),必須new一個(gè)數(shù)組
System.out.println(add(new int[0]));//數(shù)組的長(zhǎng)度為,表示沒(méi)有元素
System.out.println(sum(1));
System.out.println(sum(1,2,3));
System.out.println(sum(1,2,3,4,5));
System.out.println(sum());
System.out.println(sum(new int[]{1,2,3,4,5}));
}
/*
聲明一個(gè)方法,可以求任意個(gè)整數(shù)的和
*/
public static int add(int[] arr){
int result = 0;
for (int i = 0; i < arr.length; i++) {
result += arr[i];
}
return result;
}
public static int sum(int... arr){//把a(bǔ)rr當(dāng)成數(shù)組 int[]即可
int result = 0;
for (int i = 0; i < arr.length; i++) {
result += arr[i];
}
return result;
}
}
5.4 方法的重載(必知必會(huì))
**方法重載**:指在同一個(gè)類中,允許存在一個(gè)以上的同名方法,只要它們的參數(shù)列表不同即可,與修飾符和返回值類型無(wú)關(guān)。
- 參數(shù)列表:數(shù)據(jù)類型個(gè)數(shù)不同,數(shù)據(jù)類型不同(按理來(lái)說(shuō)數(shù)據(jù)類型順序不同也可以,但是很少見(jiàn),也不推薦,邏輯上容易有歧義)。
- 重載方法調(diào)用:JVM通過(guò)方法的參數(shù)列表,調(diào)用匹配的方法。
- 先看是否有個(gè)數(shù)、類型最匹配的
- 沒(méi)有最匹配的,再看個(gè)數(shù)和類型可以兼容的,找不到兼容的將會(huì)報(bào)錯(cuò)
案例,用重載實(shí)現(xiàn):
- (1)定義方法求兩個(gè)整數(shù)的最大值
- (2)定義方法求三個(gè)整數(shù)的最大值
- (3)定義方法求兩個(gè)小數(shù)的最大值
- (4)定義方法求n個(gè)整數(shù)最大值
public class MathTools {
//求兩個(gè)整數(shù)的最大值
public static int max(int a,int b){
return a>b?a:b;
}
//求兩個(gè)小數(shù)的最大值
public static double max(double a, double b){
return a>b?a:b;
}
//求三個(gè)整數(shù)的最大值
public static int max(int a, int b, int c){
return max(max(a,b),c);
}
//求n整數(shù)的最大值
public static int max(int[] nums){
int max = nums[0];//如果沒(méi)有傳入整數(shù),或者傳入null,這句代碼會(huì)報(bào)異常
for (int i = 1; i < nums.length; i++) {
if(nums[i] > max){
max = nums[i];
}
}
return max;
}
}
1、找最匹配的
package com.atguigu.test06.overload;
public class MethodOverloadMosthMatch {
public static void main(String[] args) {
System.out.println(MathTools.max(5,3));
System.out.println(MathTools.max(5,3,8));
System.out.println(MathTools.max(5.7,2.5));
System.out.println(MathTools.max(new int[]{5,6,8,9,7,2}));
}
}
2、找可以兼容的
public class MethodOverloadMostCompatible {
public static void main(String[] args) {
System.out.println(MathTools.max(5.7,9));
System.out.println(MathTools.max(5,6,8,3));
// System.out.println(MathTools.max(5.7,9.2,6.9)); //沒(méi)有兼容的
}
}
3、多個(gè)方法可以匹配或兼容
package com.atguigu.test06.overload;
public class MathTools {
//求兩個(gè)整數(shù)的最大值
public static int max(int a,int b){
return a>b?a:b;
}
//求兩個(gè)小數(shù)的最大值
public static double max(double a, double b){
return a>b?a:b;
}
//求三個(gè)整數(shù)的最大值
public static int max(int a, int b, int c){
return max(max(a,b),c);
}
//求n整數(shù)的最大值
public static int max(int... nums){
int max = nums[0];//如果沒(méi)有傳入整數(shù),或者傳入null,這句代碼會(huì)報(bào)異常
for (int i = 1; i < nums.length; i++) {
if(nums[i] > max){
max = nums[i];
}
}
return max;
}
/* //求n整數(shù)的最大值
public static int max(int[] nums){ //編譯就報(bào)錯(cuò),與(int... nums)無(wú)法區(qū)分
int max = nums[0];//如果沒(méi)有傳入整數(shù),或者傳入null,這句代碼會(huì)報(bào)異常
for (int i = 1, i < nums.length; i++) {
if(nums[i] > max){
max = nums[i];
}
}
return max;
}*/
/* //求n整數(shù)的最大值
public static int max(int first, int... nums){ //當(dāng)前類不報(bào)錯(cuò),但是調(diào)用時(shí)會(huì)引起多個(gè)方法同時(shí)匹配
int max = first;
for (int i = 0; i < nums.length; i++) {
if(nums[i] > max){
max = nums[i];
}
}
return max;
}*/
4、方法的重載和返回值類型無(wú)關(guān)
public class MathTools {
public static int getOneToHundred(){
return (int)(Math.random()*100);
}
public static double getOneToHundred(){
return Math.random()*100;
}
}
//以上方法不是重載
5.5 方法的遞歸調(diào)用(簡(jiǎn)單了解)
**遞歸調(diào)用**:方法自己調(diào)用自己的現(xiàn)象就稱為遞歸。
**遞歸的分類:**
- * 遞歸分為兩種,直接遞歸和間接遞歸。
- * 直接遞歸稱為方法自身調(diào)用自己。
- * 間接遞歸可以A方法調(diào)用B方法,B方法調(diào)用C方法,C方法調(diào)用A方法。
**注意事項(xiàng)**:
- * 遞歸一定要有條件限定,保證遞歸能夠停止下來(lái),否則會(huì)發(fā)生棧內(nèi)存溢出。
- * 在遞歸中雖然有限定條件,但是遞歸深度不能太深,否則效率低下,或者也會(huì)發(fā)生棧內(nèi)存溢出。
- * 能夠使用循環(huán)代替的,盡量使用循環(huán)代替遞歸
案例:計(jì)算斐波那契數(shù)列(Fibonacci)的第n個(gè)值,斐波那契數(shù)列滿足如下規(guī)律,
1,1,2,3,5,8,13,21,....
即從第三個(gè)數(shù)開(kāi)始,一個(gè)數(shù)等于前兩個(gè)數(shù)之和。假設(shè)f(n)代表斐波那契數(shù)列的第n個(gè)值,那么f(n)滿足:
f(n) = f(n-2) + f(n-1);
package com.atguigu.test07.recursion;
public class FibonacciTest {
public static void main(String[] args) {
System.out.println(f(20));//6765
System.out.println(fValue(20));//6765
System.out.println("-----------------------------");
for(int i=1; i<=10; i++){
System.out.println("斐波那契數(shù)列第" +i +"個(gè)數(shù):" + f(i));
}
System.out.println("-----------------------------");
for(int i=1; i<=10; i++){
System.out.println("斐波那契數(shù)列第" +i +"個(gè)數(shù):" + fValue(i));
}
}
//使用遞歸的寫(xiě)法
public static int f(int n){//計(jì)算斐波那契數(shù)列第n個(gè)值是多少
if(n<1){//負(fù)數(shù)是返回特殊值1,表示不計(jì)算負(fù)數(shù)情況
return 1;
}
if(n==1 || n==2){
return 1;
}
return f(n-2) + f(n-1);
}
//不用遞歸
public static int fValue(int n){//計(jì)算斐波那契數(shù)列第n個(gè)值是多少
if(n<1){//負(fù)數(shù)是返回特殊值1,表示不計(jì)算負(fù)數(shù)情況
return 1;
}
if(n==1 || n==2){
return 1;
}
//從第三個(gè)數(shù)開(kāi)始, 等于 前兩個(gè)整數(shù)相加
int beforeBefore = 1; //相當(dāng)于n=1時(shí)的值
int before = 1;//相當(dāng)于n=2時(shí)的值
int current = beforeBefore + before; //相當(dāng)于n=3的值
//再完后
for(int i=4; i<=n; i++){
beforeBefore = before;
before = current;
current = beforeBefore + before;
/*
假設(shè)i=4
beforeBefore = before; //相當(dāng)于n=2時(shí)的值
before = current; //相當(dāng)于n=3的值
current = beforeBefore + before; //相當(dāng)于n = 4的值
假設(shè)i=5
beforeBefore = before; //相當(dāng)于n=3的值
before = current; //相當(dāng)于n = 4的值
current = beforeBefore + before; //相當(dāng)于n = 5的值
....
*/
}
return current;
}
}
5.6 面向?qū)ο蟮母拍?/h2>5.6.1 面向?qū)ο缶幊趟枷敫攀觯ê?jiǎn)單了解)
1、C語(yǔ)言和Java語(yǔ)言
C語(yǔ)言是一種面向過(guò)程的程序設(shè)計(jì)語(yǔ)言,因?yàn)镃語(yǔ)言是在面向過(guò)程思想的指引下去設(shè)計(jì)、開(kāi)發(fā)計(jì)算機(jī)程序的。
Java語(yǔ)言是一種面向?qū)ο蟮某绦蛟O(shè)計(jì)語(yǔ)言,因?yàn)镴ava語(yǔ)言是在面向?qū)ο笏枷氲闹敢氯ピO(shè)計(jì)、開(kāi)發(fā)計(jì)算機(jī)程序的。
其中面向?qū)ο蠛兔嫦蜻^(guò)程都是一種編程思想,基于不同的思想會(huì)產(chǎn)生不同的程序設(shè)計(jì)方法。
1. 面向過(guò)程的程序設(shè)計(jì)思想(Process-Oriented Programming),簡(jiǎn)稱POP
- 關(guān)注的焦點(diǎn)是過(guò)程:過(guò)程就是操作數(shù)據(jù)的步驟。面向過(guò)程是分析出解決問(wèn)題所需要的步驟,不同的步驟可以抽象為一個(gè)一個(gè)的函數(shù)。
- 代碼結(jié)構(gòu):以函數(shù)為組織單位。獨(dú)立于函數(shù)之外的數(shù)據(jù)稱為全局?jǐn)?shù)據(jù),在函數(shù)內(nèi)部的稱為局部數(shù)據(jù)。
2. 面向?qū)ο蟮某绦蛟O(shè)計(jì)思想( Object Oriented Programming),簡(jiǎn)稱OOP
- 關(guān)注的焦點(diǎn)是類和對(duì)象:面向?qū)ο笏枷刖褪窃谟?jì)算機(jī)程序設(shè)計(jì)過(guò)程中,參照現(xiàn)實(shí)中事物,將事物的屬性特征、行為特征抽象出來(lái),用類來(lái)表示。某個(gè)事物的一個(gè)具體個(gè)體稱為實(shí)例或?qū)ο?。面向?qū)ο笫前褬?gòu)成問(wèn)題事務(wù)分解成各個(gè)對(duì)象,關(guān)注的是解決問(wèn)題需要哪些對(duì)象。
- 代碼結(jié)構(gòu):以類為組織單位。每種事物都具備自己的**屬性**(即表示和存儲(chǔ)數(shù)據(jù),在類中用成員變量表示)和**行為/功能**(即操作數(shù)據(jù),在類中用成員方法表示)。
2、面向過(guò)程與面向?qū)ο蟮膮^(qū)別
面向過(guò)程:
- - 優(yōu)點(diǎn)是性能比面向?qū)ο蟾撸驗(yàn)轭愓{(diào)用時(shí)需要實(shí)例化,開(kāi)銷比較大,比較消耗資源。而系統(tǒng)軟件(例如各種操作系統(tǒng))等一般采用面向過(guò)程開(kāi)發(fā),性能是最重要的因素。
- - 缺點(diǎn)是沒(méi)有面向?qū)ο笠拙S護(hù),易復(fù)用,易擴(kuò)展??删S護(hù)性差,不易修改。
面向?qū)ο螅?/p>
- - 優(yōu)點(diǎn)是易維護(hù),易復(fù)用,易擴(kuò)展。由于面向?qū)ο笥煞庋b,繼承,多態(tài)性的特性,可以設(shè)計(jì)出耦合度低的系統(tǒng),使系統(tǒng)更加靈活,更加易于維護(hù)。
- - 缺點(diǎn)是性能比面向過(guò)程低。
舉例說(shuō)明:
3 故事:非活字印刷與活字印刷
故事:略 《短歌行》曹操 對(duì)酒當(dāng)歌,人生幾何! 譬如朝露,去日苦多。 慨當(dāng)以慷,憂思難忘。 何以解憂?唯有杜康。 青青子衿,悠悠我心。 但為君故,沉吟至今。 呦呦鹿鳴,食野之蘋(píng)。 我有嘉賓,鼓瑟吹笙。 明明如月,何時(shí)可掇? 憂從中來(lái),不可斷絕。 越陌度阡,枉用相存。 契闊談讌,心念舊恩。 月明星稀,烏鵲南飛。 繞樹(shù)三匝,何枝可依? 山不厭高,海不厭深。 周公吐哺,天下歸心。 如果是有了活字印刷,則只需更改幾個(gè)字就可,其余工作都未白做。 一、要改,只需更改要改之字,此為可維護(hù); 二、這些字并非用完這次就無(wú)用,完全可以在后來(lái)的印刷中重復(fù)使用,此乃可復(fù)用; 三、此詩(shī)若要加字,只需另刻字加入即可,這是可擴(kuò)展; 四、字的排列其實(shí)可能是豎排可能是橫排,此時(shí)只需將活字移動(dòng)就可做到滿足排列需求,此是靈活性好?!?在活字印刷術(shù)出現(xiàn)之前,上面的四種特性都無(wú)法滿足,要修改,必須重刻,要加字,必須重刻,要重新排列,必須重刻,印完這本書(shū)后,此版已無(wú)任何可再利用價(jià)值。
5.6.2 類和對(duì)象的概念和關(guān)系(必知必會(huì))
1、什么是類
**類**是一類具有相同特性的事物的抽象描述,是一組相關(guān)**屬性**和**行為**的集合。
- * **屬性**:就是該事物的狀態(tài)信息。
- * **行為**:就是在你這個(gè)程序中,該狀態(tài)信息要做什么操作,或者基于事物的狀態(tài)能做什么。
2、什么是對(duì)象
**對(duì)象**是一類事物的一個(gè)具體個(gè)體(對(duì)象并不是找個(gè)女朋友)。即對(duì)象是類的一個(gè)**實(shí)例**,必然具備該類事物的屬性和行為。
3、類與對(duì)象的關(guān)系
- - 類是對(duì)一類事物的描述,是**抽象的**。
- - 對(duì)象是一類事物的實(shí)例,是**具體的**。
- - **類是對(duì)象的模板,對(duì)象是類的實(shí)體**。

5.6.3 如何定義類和創(chuàng)建對(duì)象(重點(diǎn)掌握)
1、類的定義格式
關(guān)鍵字:class(小寫(xiě))
【修飾符】 class 類名{ }
類的定義格式舉例:
public class Student{
}
2、對(duì)象的創(chuàng)建
關(guān)鍵字:new
new 類名()//也稱為匿名對(duì)象 //給創(chuàng)建的對(duì)象命名 //或者說(shuō),把創(chuàng)建的對(duì)象用一個(gè)引用數(shù)據(jù)類型的變量保存起來(lái),這樣就可以反復(fù)使用這個(gè)對(duì)象了 類名 對(duì)象名 = new 類名();
那么,對(duì)象名中存儲(chǔ)的是什么呢?答:對(duì)象地址
public class TestStudent{
public static void main(String[] args){
System.out.println(new Student());//Student@7852e922
Student stu = new Student();
System.out.println(stu);//Student@4e25154f
int[] arr = new int[5];
System.out.println(arr);//[I@70dea4e
}
}
發(fā)現(xiàn)學(xué)生對(duì)象和數(shù)組對(duì)象類似,直接打印對(duì)象名和數(shù)組名都是顯示“類型@對(duì)象的hashCode值",所以說(shuō)類、數(shù)組都是引用數(shù)據(jù)類型,引用數(shù)據(jù)類型的變量中存儲(chǔ)的是對(duì)象的地址,或者說(shuō)指向堆中對(duì)象的首地址。
那么像“Student@4e25154f”是對(duì)象的地址嗎?不是,因?yàn)镴ava是對(duì)程序員隱藏內(nèi)存地址的,不暴露內(nèi)存地址信息,所以打印對(duì)象時(shí)不直接顯示內(nèi)存地址,而是JVM幫你調(diào)用了對(duì)象的toString方法,將對(duì)象的基本信息轉(zhuǎn)換為字符串并返回,默認(rèn)toString方法返回的是“對(duì)象的運(yùn)行時(shí)類型@對(duì)象的hashCode值的十六進(jìn)制值”,程序員可以自己改寫(xiě)toString方法的代碼(后面會(huì)講如何改寫(xiě))。
5.7 Package包
5.7.1 包的作用(簡(jiǎn)單了解)
- (1)可以避免類重名:有了包之后,類的全名稱就變?yōu)椋喊?類名
(2)可以控制某些類型或成員的可見(jiàn)范圍
如果某個(gè)類型或者成員的權(quán)限修飾缺省的話,那么就僅限于本包使用。
- (3)分類組織管理眾多的類
例如:
- * java.lang----包含一些Java語(yǔ)言的核心類,如String、Math、Integer、 System和Thread等,提供常用功能
- * java.net----包含執(zhí)行與網(wǎng)絡(luò)相關(guān)的操作的類和接口。
- * java.io ----包含能提供多種輸入/輸出功能的類。
- * java.util----包含一些實(shí)用工具類,如集合框架類、日期時(shí)間、數(shù)組工具類Arrays,文本掃描儀Scanner,隨機(jī)值產(chǎn)生工具Random。
- * java.text----包含了一些java格式化相關(guān)的類
- * java.sql和javax.sql----包含了java進(jìn)行JDBC數(shù)據(jù)庫(kù)編程的相關(guān)類/接口
- * java.awt和java.swing----包含了構(gòu)成抽象窗口工具集(abstract window toolkits)的多個(gè)類,這些類被用來(lái)構(gòu)建和管理應(yīng)用程序的圖形用戶界面(GUI)。
5.7.2 如何聲明包(重點(diǎn)掌握)
關(guān)鍵字:package
package 包名;
注意:
- (1)必須在源文件的代碼首行
- (2)一個(gè)源文件只能有一個(gè)聲明包的package語(yǔ)句
包的命名規(guī)范和習(xí)慣:
- (1)所有單詞都小寫(xiě),每一個(gè)單詞之間使用.分割
- (2)習(xí)慣用公司的域名倒置開(kāi)頭
- (3)不能以"java."開(kāi)頭
例如:com.atguigu.xxx;
5.7.3 如何跨包使用類(重點(diǎn)掌握)
注意:只有public的類才能被跨包使用
(1)使用類型的全名稱
例如:java.util.Scanner input = new java.util.Scanner(System.in);
(2)使用import 語(yǔ)句之后,代碼中使用簡(jiǎn)名稱
import語(yǔ)句告訴編譯器到哪里去尋找類。
import語(yǔ)句的語(yǔ)法格式:
import 包.類名; import 包.*;
注意:
- 使用java.lang包下的類,不需要import語(yǔ)句,就直接可以使用簡(jiǎn)名稱
- import語(yǔ)句必須在package下面,class的上面
- 當(dāng)使用兩個(gè)不同包的同名類時(shí),例如:java.util.Date和java.sql.Date。一個(gè)使用全名稱,一個(gè)使用簡(jiǎn)名稱
package com.atguigu.test02.pkg;
import com.atguigu.test01.oop.Student;
import java.util.Date;
import java.util.Scanner;
public class TestPackage {
public static void main(String[] args) {
/* java.util.Scanner input = new java.util.Scanner(System.in);
com.atguigu.test01.oop.Student stu = new com.atguigu.test01.oop.Student();*/
Scanner input = new Scanner(System.in);
Student student = new Student();
Date d1 = new Date();
java.sql.Date d2 = new java.sql.Date(0);
}
}
5.7.4 靜態(tài)導(dǎo)入(簡(jiǎn)單了解)
如果大量使用另一個(gè)類的靜態(tài)成員,可以使用靜態(tài)導(dǎo)入,簡(jiǎn)化代碼。
import static 包.類名.靜態(tài)成員名; import static 包.類名.*;
演示:
package com.atguigu.keyword;
import static java.lang.Math.*;
public class TestStaticImport {
public static void main(String[] args) {
//使用Math類的靜態(tài)成員
System.out.println(Math.PI);
System.out.println(Math.sqrt(9));
System.out.println(Math.random());
System.out.println("----------------------------");
System.out.println(PI);
System.out.println(sqrt(9));
System.out.println(random());
}
}
5.8 類的成員之一:成員變量(重點(diǎn)掌握)
5.8.1 成員變量的分類
Java類的成員變量分為兩大類:
- - 靜態(tài)變量(加staitc修飾),又稱為類變量
- - 非靜態(tài)變量(不加static修飾),又稱為實(shí)例變量或者屬性。
5.8.2 成員變量之實(shí)例變量
1、實(shí)例變量的聲明格式
【修飾符】 class 類名{ 【修飾符】 數(shù)據(jù)類型 成員變量名; }
位置要求:必須在類中,方法外
類型要求:可以是Java的任意類型,包括基本數(shù)據(jù)類型、引用數(shù)據(jù)類型(類、接口、數(shù)組等)
修飾符:暫時(shí)寫(xiě)public。
public class Person{
public String name;
public char gender;
public int age;
}
2、實(shí)例變量的特點(diǎn)
(1)實(shí)例變量的值是屬于某個(gè)對(duì)象的
- 在類的外面必須通過(guò)對(duì)象才能訪問(wèn)實(shí)例變量
- 每個(gè)對(duì)象的實(shí)例變量的值是獨(dú)立的
- (2)實(shí)例變量有默認(rèn)值
分類 | 數(shù)據(jù)類型 | 默認(rèn)值 |
---|---|---|
基本類型 | 整數(shù)(byte,short,int,long) | 0 |
浮點(diǎn)數(shù)(float,double) | 0.0 | |
字符(char) | '\u0000' | |
布爾(boolean) | false | |
引用類型 | 數(shù)組,類,接口 | null |
3、實(shí)例變量的訪問(wèn)
對(duì)象.實(shí)例變量
例如:
package com.atguigu.test03.field;
public class TestPerson {
public static void main(String[] args) {
Person p1 = new Person();
p1.name = "張三";
p1.age = 23;
p1.gender = '男';
Person p2 = new Person();
/*
(1)實(shí)例變量的值是屬于某個(gè)對(duì)象的
- 必須通過(guò)對(duì)象才能訪問(wèn)實(shí)例變量
- 每個(gè)對(duì)象的實(shí)例變量的值是獨(dú)立的
(2)實(shí)例變量有默認(rèn)值
*/
System.out.println("p1對(duì)象的實(shí)例變量:");
System.out.println("p1.name = " + p1.name);
System.out.println("p1.age = " + p1.age);
System.out.println("p1.gender = " + p1.gender);
System.out.println("p2對(duì)象的實(shí)例變量:");
System.out.println("p2.name = " + p2.name);
System.out.println("p2.age = " + p2.age);
System.out.println("p2.gender = " + p2.gender);
}
}
4、實(shí)例變量的內(nèi)存分析
內(nèi)存是計(jì)算機(jī)中重要的部件之一,它是與CPU進(jìn)行溝通的橋梁。其作用是用于暫時(shí)存放CPU中的運(yùn)算數(shù)據(jù),以及與硬盤(pán)等外部存儲(chǔ)器交換的數(shù)據(jù)。只要計(jì)算機(jī)在運(yùn)行中,CPU就會(huì)把需要運(yùn)算的數(shù)據(jù)調(diào)到內(nèi)存中進(jìn)行運(yùn)算,當(dāng)運(yùn)算完成后CPU再將結(jié)果傳送出來(lái)。我們編寫(xiě)的程序是存放在硬盤(pán)中的,在硬盤(pán)中的程序是不會(huì)運(yùn)行的,必須放進(jìn)內(nèi)存中才能運(yùn)行,運(yùn)行完畢后會(huì)清空內(nèi)存。Java虛擬機(jī)要運(yùn)行程序,必須要對(duì)內(nèi)存進(jìn)行空間的分配和管理,每一片區(qū)域都有特定的處理數(shù)據(jù)方式和內(nèi)存管理方式。
JVM的運(yùn)行時(shí)內(nèi)存區(qū)域分為:方法區(qū)、堆、虛擬機(jī)棧、本地方法棧、程序計(jì)數(shù)器幾大塊。
區(qū)域名稱 | 作用 |
---|---|
程序計(jì)數(shù)器 | 程序計(jì)數(shù)器是CPU中的寄存器,它包含每一個(gè)線程下一條要執(zhí)行的指令的地址 |
本地方法棧 | 當(dāng)程序中調(diào)用了native的本地方法時(shí),本地方法執(zhí)行期間的內(nèi)存區(qū)域 |
方法區(qū) | 存儲(chǔ)已被虛擬機(jī)加載的類信息、常量、靜態(tài)變量、即時(shí)編譯器編譯后的代碼等數(shù)據(jù)。 |
堆內(nèi)存 | 存儲(chǔ)對(duì)象(包括數(shù)組對(duì)象),new來(lái)創(chuàng)建的,都存儲(chǔ)在堆內(nèi)存。 |
虛擬機(jī)棧 | 用于存儲(chǔ)正在執(zhí)行的每個(gè)Java方法的局部變量表等。局部變量表存放了編譯期可知長(zhǎng)度的各種基本數(shù)據(jù)類型、對(duì)象引用,方法執(zhí)行完,自動(dòng)釋放。 |
Java對(duì)象保存在內(nèi)存中時(shí),由以下三部分組成:
- 對(duì)象頭
- Mark Word:記錄了和當(dāng)前對(duì)象有關(guān)的GC、鎖等信息。(Java高級(jí)再詳細(xì)講)
- 指向類的指針:每一個(gè)對(duì)象需要記錄它是由哪個(gè)類創(chuàng)建出來(lái)的,而Java對(duì)象的類數(shù)據(jù)保存在方法區(qū),指向類的指針就是記錄創(chuàng)建該對(duì)象的類數(shù)據(jù)在方法區(qū)的首地址。該指針在32位JVM中的長(zhǎng)度是32bit,在64位JVM中長(zhǎng)度是64bit。
- 數(shù)組長(zhǎng)度(只有數(shù)組對(duì)象才有)
- 實(shí)例數(shù)據(jù)
- 即實(shí)例變量的值
- 對(duì)齊填充
- 因?yàn)镴VM要求Java對(duì)象占的內(nèi)存大小應(yīng)該是8bit的倍數(shù),如果不滿足該大小,則需要補(bǔ)齊至8bit的倍數(shù),沒(méi)有特別的功能。
5.8.3 成員變量之靜態(tài)變量
1、靜態(tài)變量聲明格式
有static修飾的成員變量就是靜態(tài)變量。
【修飾符】 class 類{ 【其他修飾劑】 static 數(shù)據(jù)類型 靜態(tài)變量名; }
public class Chinese{
public static String country; //靜態(tài)變量
public String name; //實(shí)例變量
}
2、靜態(tài)變量的特點(diǎn)
- - 靜態(tài)變量的默認(rèn)值規(guī)則和實(shí)例變量一樣。
- - 靜態(tài)變量值是所有對(duì)象共享。
3、靜態(tài)變量的訪問(wèn)
在其他類中可以通過(guò)“類名.靜態(tài)變量”直接訪問(wèn),也可以通過(guò)“對(duì)象.靜態(tài)變量”的方式訪問(wèn)(但是更推薦使用類名.靜態(tài)變量的方式)。
類名.靜態(tài)變量
public class TestChinese{
public static void main(String[] args){
Chinese.country = "中國(guó)";
Chinese c1 = new Chinese();
c1.name = "張三";
Chinese c2 = new Chinese();
System.out.println(Chinese.country);
System.out.println(c1.country + "," + c1.name);
System.out.println(c2.country + "," + c2.name);
System.out.println("=======================");
c1.country = "china";
System.out.println(Chinese.country);
System.out.println(c1.country + "," + c1.name);
System.out.println(c2.country + "," + c2.name);
}
}
4、靜態(tài)變量?jī)?nèi)存分析
- 靜態(tài)變量的值存儲(chǔ)在方法區(qū)。
具體內(nèi)存分析圖請(qǐng)看PPT。
5.8.4 成員變量是引用數(shù)據(jù)類型
成員變量也是變量,數(shù)據(jù)類型可以是8種基本數(shù)據(jù)類型,也可以是引用數(shù)據(jù)類型(數(shù)組、類等)。如果是引用數(shù)據(jù)類型,請(qǐng)警惕空指針異常。
class Husband{
String name;
Wife wife;
}
class Wife{
String name;
Husband husband;
}
class TestMarry{
public static void main(String[] args){
Husband h = new Husband();
h.name = "張三";
Wife w = new Wife();
w.name = "翠花";
System.out.println("丈夫的姓名:" + h.name + ",妻子:" + h.wife);
h.wife = w;
System.out.println("丈夫的姓名:" + h.name + ",妻子:" + h.wife.name);//警惕空指針異常
System.out.println("妻子的姓名:" + w.name + ",丈夫:" + w.husband);
w.husband = h;
System.out.println("妻子的姓名:" + w.name + ",丈夫:" + w.husband.name);//警惕空指針異常
System.out.println("---------------------------------------");
//離婚
h.wife = null;
w.husband = null;
h.wife = new Wife();
h.wife.name = "小何";
h.wife.husband = h;
System.out.println("丈夫的姓名:" + h.name + ",妻子:" + h.wife.name);
System.out.println("妻子的姓名:" + h.wife.name + ",丈夫:" + h.wife.husband.name);
}
}
總結(jié):
編譯時(shí):一個(gè)引用數(shù)據(jù)類型的變量,可以.出什么,和這個(gè)變量的類型有關(guān),這個(gè)類型中有什么成員,就可以.出什么成員。
運(yùn)行時(shí):.操作是不是會(huì)發(fā)生空指針異常,要看.前面的變量有沒(méi)有“引用”一個(gè)對(duì)象,即有沒(méi)有給它賦值一個(gè)對(duì)象。
5.9 類的成員之二:成員方法(重點(diǎn)掌握)
5.9.1 類的靜態(tài)方法
靜態(tài)方法不依賴于對(duì)象。
1、在靜態(tài)方法中直接使用本類的靜態(tài)變量
當(dāng)靜態(tài)變量與局部變量同名時(shí),可以用“類名.靜態(tài)變量”進(jìn)行區(qū)分。
public class Demo1{
public static int a;
public static int b;
public static void main(String[] args){
System.out.println("a = " + a);
int b = 1;
System.out.println("b = " + b);
System.out.println("Demo1.b = " + Demo1.b);
}
}
2、在靜態(tài)方法中直接調(diào)用本類的靜態(tài)方法
public class Demo2{
public static void say(){
System.out.println("hello");
}
public static void main(String[] args){
say();
}
}
3、在其他類中調(diào)用靜態(tài)方法
public class Demo3{
public static void show(){
System.out.println("hello");
}
}
public class Demo3Test{
public static void main(String[] args){
Demo3.show();
}
}
5.9.2 對(duì)象的實(shí)例方法
1、在實(shí)例方法中直接使用當(dāng)前對(duì)象的實(shí)例變量
實(shí)例方法依賴于對(duì)象,因?yàn)閷?shí)例方法必須由對(duì)象來(lái)調(diào)用,那么調(diào)用當(dāng)前方法的對(duì)象稱為當(dāng)前對(duì)象,在實(shí)例方法中使用this表示。
如果當(dāng)前實(shí)例方法中沒(méi)有局部變量與實(shí)例變量重名,也可以省略this.,如果有局部變量與實(shí)例變量重名,就必須加this.。
package com.atguigu.exer3;
//(1)聲明一個(gè)MyDate類型,有屬性:年,月,日
public class MyDate {
public int year;
public int month;
public int day;
public void setValue(int year, int month,int day){
this.year = year;
this.month = month;
this.day = day;
}
public String getInfo(){
return year+"年" + month+"月" + day+"日";
}
}
2、在實(shí)例方法中直接使用本類的靜態(tài)變量
public class Chinese{
public static String country; //靜態(tài)變量
public String name; //實(shí)例變量
public String getInfo(){
return "country = " + country +",name = " + name;
}
}
靜態(tài)變量,又稱為類變量,是當(dāng)前所有類的對(duì)象共享的。
3、在實(shí)例方法中直接調(diào)用本類的靜態(tài)方法
public class Chinese{
public static String country; //靜態(tài)變量
public String name; //實(shí)例變量
public static void sayCountry(){
System.out.println("我是一個(gè)中國(guó)人");
}
public void say(){
sayCountry();
System.out.println("我的名字是:" + name);
}
}
4、在實(shí)例方法中直接調(diào)用本類的實(shí)例方法
public class Chinese{
public static String country; //靜態(tài)變量
public String name; //實(shí)例變量
public static void sayCountry(){
System.out.println("我是一個(gè)中國(guó)人");
}
public void sayName(){
System.out.println("我的名字是:" + name);
}
public void say(){
sayCountry();
sayName();
}
}
5、其他類中調(diào)用實(shí)例方法
當(dāng)出現(xiàn)某個(gè)類的多個(gè)對(duì)象都要進(jìn)行相同操作時(shí),這些操作的重復(fù)性代碼,就可以封裝為實(shí)例方法。
在其他類中調(diào)用實(shí)例方法:
對(duì)象名.實(shí)例方法(【實(shí)參列表】)
例如:
//1、創(chuàng)建Scanner的對(duì)象
Scanner input = new Scanner(System.in);//System.in默認(rèn)代表鍵盤(pán)輸入
//2、提示輸入xx
System.out.print("請(qǐng)輸入一個(gè)整數(shù):"); //對(duì)象.非靜態(tài)方法(實(shí)參列表)
//3、接收輸入內(nèi)容
int num = input.nextInt(); //對(duì)象.非靜態(tài)方法()
案例演示:
package com.atguigu.exer3;
public class MyDate {
public int year;
public int month;
public int day;
public void setValue(int year, int month,int day){
this.year = year;
this.month = month;
this.day = day;
}
public String getInfo(){
return year+"年" + month+"月" + day+"日";
}
}
package com.atguigu.exer3;
public class Employee {
public String name;
public MyDate birthday;
public void setBirthday(int year, int month, int day){
birthday = new MyDate();
// birthday.year = year;
// birthday.month = month;
// birthday.day = day;
birthday.setValue(year, month, day);
}
public String getInfo(){
// return "姓名:" + name +",生日:" + birthday.year+"年" + birthday.month+"月" + birthday.day+"日";
return "姓名:" + name +",生日:" + birthday.getInfo();
}
}
public class EmployeeTest {
public static void main(String[] args) {
Employee e1 = new Employee();
e1.name = "張三";
// e1.birthday = new MyDate();
// e1.birthday.year = 1990;
// e1.birthday.month = 5;
// e1.birthday.day = 1;
e1.setBirthday(1990,5,1);
// System.out.println("e1的姓名:" + e1.name +",生日:" + e1.birthday.year+"年" + e1.birthday.month+"月" + e1.birthday.day+"日");
System.out.println("e1的" + e1.getInfo());
Employee e2 = new Employee();
e2.name = "李四";
// e2.birthday = new MyDate();
// e2.birthday.year = 1995;
// e2.birthday.month = 6;
// e2.birthday.day = 1;
e2.setBirthday(1996,6,1);
// System.out.println("e2的姓名:" + e2.name +",生日:" + e2.birthday.year+"年" + e2.birthday.month+"月" + e2.birthday.day+"日");
System.out.println("e2的" + e2.getInfo());
}
}
6、實(shí)例方法的內(nèi)存分析
實(shí)例方法是由實(shí)例對(duì)象調(diào)用的,每一個(gè)實(shí)例方法中默認(rèn)有一個(gè)this變量用來(lái)記錄當(dāng)前對(duì)象(即調(diào)用該方法的實(shí)例對(duì)象)的首地址。
調(diào)用過(guò)程分析請(qǐng)看PPT。
5.9.3 成員互訪原則
靜態(tài)變量 | 實(shí)例變量 | 靜態(tài)方法 | 實(shí)例方法 | ||
---|---|---|---|---|---|
本類中 | 在靜態(tài)方法中 | 直接使用 | × | 直接使用 | × |
在實(shí)例方法中 | 直接使用 | 直接使用 | 直接使用 | 直接使用 | |
在其他類中 | 類名.靜態(tài)變量(推薦) | 對(duì)象名.實(shí)例變量 | 類名.靜態(tài)方法(推薦) | 對(duì)象名.實(shí)例方法 | |
對(duì)象名.靜態(tài)變量(不推薦) | 對(duì)象名.靜態(tài)方法(不推薦) |
5.9.4 各種變量小結(jié)
- 靜態(tài)類變量(簡(jiǎn)稱靜態(tài)變量):存儲(chǔ)在方法區(qū),有默認(rèn)值,所有對(duì)象共享,生命周期和類相同,還可以有權(quán)限修飾符等其他修飾符
- 非靜態(tài)實(shí)例變量(簡(jiǎn)稱實(shí)例變量):存儲(chǔ)在堆中,有默認(rèn)值,每一個(gè)對(duì)象獨(dú)立,生命周期每一個(gè)對(duì)象也獨(dú)立,還可以有權(quán)限修飾符等其他修飾符
- 局部變量:存儲(chǔ)在棧中,沒(méi)有默認(rèn)值,每一次方法調(diào)用都是獨(dú)立的,有作用域,不能加權(quán)限修飾符
請(qǐng)看PPT。
5.10 構(gòu)造器(重點(diǎn)掌握)
我們發(fā)現(xiàn)我們new完對(duì)象時(shí),所有成員變量都是默認(rèn)值,如果我們需要賦別的值,需要挨個(gè)為它們?cè)儋x值,太麻煩了。我們能不能在new對(duì)象時(shí),直接為當(dāng)前對(duì)象的某個(gè)或所有成員變量直接賦值呢。
可以,Java給我們提供了構(gòu)造器(Constructor)。
5.10.1 構(gòu)造器的作用
new對(duì)象,并在new對(duì)象的時(shí)候?yàn)閷?shí)例變量賦值。
5.10.2 聲明構(gòu)造器
構(gòu)造器又稱為構(gòu)造方法,構(gòu)造函數(shù),那是因?yàn)樗L(zhǎng)的很像方法。但是和方法還是有所區(qū)別的。
1、構(gòu)造器語(yǔ)法格式和要求
【修飾符】 class 類名{ 【修飾符】 構(gòu)造器名(){ // 實(shí)例初始化代碼 } 【修飾符】 構(gòu)造器名(參數(shù)列表){ // 實(shí)例初始化代碼 } }
注意事項(xiàng):
- 1. 構(gòu)造器名必須與它所在的類名必須相同。
- 2. 它沒(méi)有返回值,所以不需要返回值類型,甚至不需要void
- 3. 如果你不提供構(gòu)造器,系統(tǒng)會(huì)給出無(wú)參數(shù)構(gòu)造器,并且該構(gòu)造器的修飾符默認(rèn)與類的修飾符相同
- 4. 如果你提供了構(gòu)造器,系統(tǒng)將不再提供無(wú)參數(shù)構(gòu)造器,除非你自己定義。
- 5. 構(gòu)造器是可以重載的,既可以定義參數(shù),也可以不定義參數(shù)。
- 6. 構(gòu)造器的修飾符只能是權(quán)限修飾符,不能被其他任何修飾
package com.atguigu.constructor;
public class Student {
public String name;
public int age;
// 無(wú)參構(gòu)造
public Student() {}
// 有參構(gòu)造
public Student(String name,int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
2、構(gòu)造器的調(diào)用時(shí)機(jī)
當(dāng)使用new關(guān)鍵字創(chuàng)建對(duì)象時(shí),就會(huì)自動(dòng)調(diào)用對(duì)應(yīng)的構(gòu)造器來(lái)完成對(duì)象的初始化工作。例如:
public class TestStudent {
public static void main(String[] args) {
// 調(diào)用無(wú)參構(gòu)造器創(chuàng)建對(duì)象
Student s1 = new Student();
s1.name = "張三";
s1.age = 20;
// 調(diào)用有參構(gòu)造器創(chuàng)建對(duì)象
Student s2 = new Student("李四", 22);
System.out.println(s1);
System.out.println(s2);
}
}
5.10.3 構(gòu)造器的重載
與方法重載類似,在同一個(gè)類中可以定義多個(gè)構(gòu)造器,只要它們的參數(shù)列表不同即可,目的是可以通過(guò)不同的參數(shù)組合來(lái)創(chuàng)建對(duì)象并進(jìn)行不同的初始化操作。比如:
public class Person {
private String name;
private int age;
private String address;
// 無(wú)參構(gòu)造器
public Person() {}
// 帶有姓名和年齡參數(shù)的構(gòu)造器
public Person(String name, int age) {
this.name = name;
this.age = age;
}
// 帶有姓名、年齡和地址參數(shù)的構(gòu)造器
public Person(String name, int age, String address) {
this.name = name;
this.age = age;
this.address = address;
}
}
調(diào)用示例:
public class TestPerson {
public static void main(String[] args) {
// 使用無(wú)參構(gòu)造器創(chuàng)建對(duì)象
Person p1 = new Person();
// 使用帶有姓名和年齡參數(shù)的構(gòu)造器創(chuàng)建對(duì)象
Person p2 = new Person("王五", 30);
// 使用帶有姓名、年齡和地址參數(shù)的構(gòu)造器創(chuàng)建對(duì)象
Person p3 = new Person("趙六", 35, "北京");
// 后續(xù)可以對(duì)這些對(duì)象進(jìn)行相應(yīng)操作
}
}
5.10.4 構(gòu)造器的執(zhí)行順序(當(dāng)存在繼承關(guān)系時(shí))
如果類之間存在繼承關(guān)系,那么在創(chuàng)建子類對(duì)象時(shí),構(gòu)造器的調(diào)用順序是先調(diào)用父類的構(gòu)造器(默認(rèn)調(diào)用無(wú)參構(gòu)造器,如果父類沒(méi)有無(wú)參構(gòu)造器,需要在子類構(gòu)造器中通過(guò)super關(guān)鍵字顯式調(diào)用父類有參構(gòu)造器),再調(diào)用子類的構(gòu)造器。例如:
class Animal {
public Animal() {
System.out.println("父類Animal的構(gòu)造器被調(diào)用");
}
}
class Dog extends Animal {
public Dog() {
System.out.println("子類Dog的構(gòu)造器被調(diào)用");
}
}
測(cè)試代碼:
public class TestConstructorOrder {
public static void main(String[] args) {
Dog dog = new Dog();
}
}
運(yùn)行結(jié)果會(huì)先輸出“父類Animal的構(gòu)造器被調(diào)用”,再輸出“子類Dog的構(gòu)造器被調(diào)用”,這體現(xiàn)了在對(duì)象初始化時(shí),先完成父類部分的初始化,再進(jìn)行子類部分的初始化。
5.10.5 用構(gòu)造器進(jìn)行初始化的優(yōu)勢(shì)
相較于手動(dòng)一個(gè)個(gè)給對(duì)象的成員變量賦值,使用構(gòu)造器進(jìn)行初始化有以下好處:
- 1. 代碼更加簡(jiǎn)潔和規(guī)范,集中在構(gòu)造器中完成對(duì)象初始值的設(shè)置,便于代碼的閱讀和維護(hù)。例如,當(dāng)創(chuàng)建一個(gè)復(fù)雜對(duì)象,有多個(gè)成員變量需要賦值時(shí),通過(guò)構(gòu)造器可以清晰地看到初始化邏輯。
- 2. 可以保證對(duì)象在創(chuàng)建出來(lái)后處于一個(gè)合理的初始狀態(tài),避免因忘記給某些關(guān)鍵成員變量賦值而導(dǎo)致后續(xù)程序出現(xiàn)錯(cuò)誤。比如,一個(gè)表示學(xué)生的類,通過(guò)構(gòu)造器可以確保每個(gè)學(xué)生對(duì)象創(chuàng)建時(shí),姓名、學(xué)號(hào)等重要信息都有初始值。
- 3. 利用構(gòu)造器的重載,可以靈活地根據(jù)不同場(chǎng)景創(chuàng)建具有不同初始值的對(duì)象,滿足多樣化的業(yè)務(wù)需求。
5.11 代碼塊(可選拓展)
5.11.1 代碼塊的分類
在Java中代碼塊主要分為以下幾種類型:
- 1. **靜態(tài)代碼塊**:用static關(guān)鍵字修飾的代碼塊,在類加載的時(shí)候執(zhí)行,且只執(zhí)行一次,通常用于初始化靜態(tài)變量等操作。例如:
public class StaticBlockDemo {
static int num;
static {
num = 10;
System.out.println("靜態(tài)代碼塊執(zhí)行,初始化靜態(tài)變量num的值為" + num);
}
}
- 2. **實(shí)例代碼塊**:也叫非靜態(tài)代碼塊,沒(méi)有static關(guān)鍵字修飾,每次創(chuàng)建對(duì)象時(shí)都會(huì)執(zhí)行,在構(gòu)造器之前執(zhí)行,常用于初始化實(shí)例變量等操作。例如:
public class InstanceBlockDemo {
private int count;
{
count = 5;
System.out.println("實(shí)例代碼塊執(zhí)行,初始化實(shí)例變量count的值為" + count);
}
public InstanceBlockDemo() {
System.out.println("構(gòu)造器執(zhí)行");
}
}
- 3. **局部代碼塊**:定義在方法內(nèi)部的代碼塊,用于限定變量的作用域,當(dāng)代碼塊執(zhí)行結(jié)束后,其中定義的變量就會(huì)被銷毀,可節(jié)省內(nèi)存空間等。例如:
public class LocalBlockDemo {
public void testMethod() {
{
int localVar = 20;
System.out.println("局部代碼塊內(nèi)的變量localVar的值為" + localVar);
}
// 此處如果再訪問(wèn)localVar會(huì)報(bào)錯(cuò),因?yàn)槌隽似渥饔糜? }
}
5.11.2 代碼塊的執(zhí)行順序(當(dāng)存在多種代碼塊及繼承關(guān)系時(shí))
當(dāng)一個(gè)類中既有靜態(tài)代碼塊,又有實(shí)例代碼塊,還有構(gòu)造器,并且類之間存在繼承關(guān)系時(shí),執(zhí)行順序如下:
- 1. 首先執(zhí)行父類的靜態(tài)代碼塊(只執(zhí)行一次,在類第一次被加載時(shí)執(zhí)行)。
- 2. 接著執(zhí)行子類的靜態(tài)代碼塊(同樣只執(zhí)行一次,在子類類加載時(shí)執(zhí)行)。
- 3. 然后在創(chuàng)建子類對(duì)象時(shí),先執(zhí)行父類的實(shí)例代碼塊,再執(zhí)行父類的構(gòu)造器。
- 4. 最后執(zhí)行子類的實(shí)例代碼塊,再執(zhí)行子類的構(gòu)造器。
以下是示例代碼展示這種執(zhí)行順序:
class Parent {
static {
System.out.println("父類靜態(tài)代碼塊執(zhí)行");
}
{
System.out.println("父類實(shí)例代碼塊執(zhí)行");
}
public Parent() {
System.out.println("父類構(gòu)造器執(zhí)行");
}
}
class Child extends Parent {
static {
System.out.println("子類靜態(tài)代碼塊執(zhí)行");
}
{
System.out.println("子類實(shí)例代碼塊執(zhí)行");
}
public Child() {
System.out.println("子類構(gòu)造器執(zhí)行");
}
}
測(cè)試代碼:
public class BlockOrderTest {
public static void main(String[] args) {
Child child = new Child();
}
}
運(yùn)行上述代碼,會(huì)按照前面所述的順序依次輸出相應(yīng)的執(zhí)行語(yǔ)句,清晰地展示了各代碼塊在對(duì)象創(chuàng)建及類加載過(guò)程中的執(zhí)行順序。
5.11.3 代碼塊的作用及應(yīng)用場(chǎng)景
1. **靜態(tài)代碼塊的作用及應(yīng)用場(chǎng)景**:
- - **作用**:主要用于初始化類級(jí)別的靜態(tài)資源,比如加載配置文件、初始化數(shù)據(jù)庫(kù)連接池等,因?yàn)檫@些操作只需要在類加載時(shí)進(jìn)行一次即可。它還可以用于對(duì)靜態(tài)變量進(jìn)行一些復(fù)雜的初始化邏輯設(shè)置,確保靜態(tài)變量在被使用前已經(jīng)有合適的初始值。
- - **應(yīng)用場(chǎng)景**:例如在一個(gè)數(shù)據(jù)庫(kù)操作類中,通過(guò)靜態(tài)代碼塊來(lái)加載數(shù)據(jù)庫(kù)驅(qū)動(dòng),代碼可能如下:
import java.sql.DriverManager;
import java.sql.SQLException;
public class DatabaseUtil {
static {
try {
Class.forName("com.mysql.jdbc.Driver");
System.out.println("數(shù)據(jù)庫(kù)驅(qū)動(dòng)加載成功");
} catch (ClassNotFoundException e) {
System.out.println("數(shù)據(jù)庫(kù)驅(qū)動(dòng)加載失敗");
e.printStackTrace();
}
}
// 后續(xù)可以定義其他數(shù)據(jù)庫(kù)操作相關(guān)的方法等
}
2. **實(shí)例代碼塊的作用及應(yīng)用場(chǎng)景**:
- - **作用**:常用于初始化實(shí)例對(duì)象的一些通用屬性,尤其是那些每次創(chuàng)建對(duì)象都需要進(jìn)行相同初始化操作的情況。它可以對(duì)實(shí)例變量進(jìn)行賦值等操作,確保對(duì)象創(chuàng)建出來(lái)后部分屬性有初始值,而且可以在其中編寫(xiě)一些相對(duì)獨(dú)立于構(gòu)造器但又需要在構(gòu)造器之前執(zhí)行的邏輯。
- - **應(yīng)用場(chǎng)景**:比如創(chuàng)建一個(gè)表示商品的類,商品有默認(rèn)的庫(kù)存數(shù)量等屬性,通過(guò)實(shí)例代碼塊可以在每次創(chuàng)建商品對(duì)象時(shí)將庫(kù)存數(shù)量初始化為一個(gè)固定值,示例如下:
public class Product {
private int stock;
{
stock = 100;
System.out.println("實(shí)例代碼塊執(zhí)行,將商品庫(kù)存初始化為" + stock);
}
public Product() {
// 構(gòu)造器中可以繼續(xù)其他初始化操作或者什么都不做
}
// 可以定義其他與商品相關(guān)的方法等
}
3. **局部代碼塊的作用及應(yīng)用場(chǎng)景**:
- - **作用**:通過(guò)限定變量的作用域來(lái)節(jié)省內(nèi)存空間,尤其是在方法內(nèi)部對(duì)于一些臨時(shí)使用且占用較大內(nèi)存空間的變量,使用局部代碼塊可以讓這些變量在使用完后及時(shí)釋放內(nèi)存。同時(shí)也可以讓代碼的邏輯結(jié)構(gòu)更加清晰,將一組相關(guān)的代碼和變量限定在一個(gè)局部范圍內(nèi),便于閱讀和理解。
- - **應(yīng)用場(chǎng)景**:比如在一個(gè)循環(huán)中需要?jiǎng)?chuàng)建一個(gè)臨時(shí)的大容量數(shù)組來(lái)存儲(chǔ)數(shù)據(jù),但這個(gè)數(shù)組在循環(huán)結(jié)束后就不再需要了,就可以將數(shù)組的定義和使用放在局部代碼塊內(nèi),示例如下:
public class LoopDemo {
public void loopMethod() {
for (int i = 0; i < 10; i++) {
{
int[] tempArray = new int[10000];
// 使用tempArray進(jìn)行一些數(shù)據(jù)處理等操作
}
// 此處tempArray已經(jīng)超出作用域被銷毀,節(jié)省了內(nèi)存空間
}
}
}