方法(有的人喜歡叫函數)是一段可重用的代碼段。html
:notebook: 本文已歸檔到:「blog」java
:keyboard: 本文中的示例代碼已歸檔到:「javacore」git
方法定義語法格式:github
[修飾符] 返回值類型 方法名([參數類型 參數名]){
...
方法體
...
return 返回值;
}
複製代碼
示例:算法
public static void main(String[] args) {
System.out.println("Hello World");
}
複製代碼
方法包含一個方法頭和一個方法體。下面是一個方法的全部部分:編程
return;
這種形式。當程序調用一個方法時,程序的控制權交給了被調用的方法。當被調用方法的返回語句執行或者到達方法體閉括號時候交還控制權給程序。設計模式
Java 支持兩種調用方法的方式,根據方法是否有返回值來選擇。bash
int larger = max(30, 40);
複製代碼
System.out.println("Hello World");
複製代碼
Java 支持方法的遞歸調用(即方法調用自身)。併發
注意:編程語言
- 遞歸方法必須有明確的結束條件。
- 儘可能避免使用遞歸調用。由於遞歸調用若是處理不當,可能致使棧溢出。
斐波那契數列(一個典型的遞歸算法)示例:
public class RecursionMethodDemo {
public static int fib(int num) {
if (num == 1 || num == 2) {
return 1;
} else {
return fib(num - 2) + fib(num - 1);
}
}
public static void main(String[] args) {
for (int i = 1; i < 10; i++) {
System.out.print(fib(i) + "\t");
}
}
}
複製代碼
在 C/C++ 等編程語言中,方法的參數傳遞通常有兩種形式:
那麼,Java 中是怎樣的呢?
Java 中只有值傳遞。
示例一:
public class MethodParamDemo {
public static void method(int value) {
value = value + 1;
}
public static void main(String[] args) {
int num = 0;
method(num);
System.out.println("num = [" + num + "]");
method(num);
System.out.println("num = [" + num + "]");
}
}
// Output:
// num = [0]
// num = [0]
複製代碼
示例二:
public class MethodParamDemo2 {
public static void method(StringBuilder sb) {
sb = new StringBuilder("B");
}
public static void main(String[] args) {
StringBuilder sb = new StringBuilder("A");
System.out.println("sb = [" + sb.toString() + "]");
method(sb);
System.out.println("sb = [" + sb.toString() + "]");
sb = new StringBuilder("C");
System.out.println("sb = [" + sb.toString() + "]");
}
}
// Output:
// sb = [A]
// sb = [A]
// sb = [C]
複製代碼
說明:
以上兩個示例,不管向方法中傳入的是基礎數據類型,仍是引用類型,在方法中修改的值,在外部都未生效。
Java 對於基本數據類型,會直接拷貝值傳遞到方法中;對於引用數據類型,拷貝當前對象的引用地址,而後把該地址傳遞過去,因此也是值傳遞。
擴展閱讀:
前面提到了,Java 方法的修飾符是可選的,它告訴編譯器如何調用該方法。定義了該方法的訪問類型。
Java 方法有好幾個修飾符,讓咱們一一來認識一下:
訪問權限控制的等級,從最大權限到最小權限依次爲:
public > protected > 包訪問權限(沒有任何關鍵字)> private
複製代碼
public
- 表示任何類均可以訪問;包訪問權限
- 包訪問權限,沒有任何關鍵字。它表示當前包中的全部其餘類均可以訪問,可是其它包的類沒法訪問。protected
- 表示子類能夠訪問,此外,同一個包內的其餘類也能夠訪問,即便這些類不是子類。private
- 表示其它任何類都沒法訪問。被 static
修飾的方法被稱爲靜態方法。
靜態方法相比於普通的實例方法,主要有如下區別:
在外部調用靜態方法時,可使用 類名.方法名
的方式,也可使用 對象名.方法名
的方式。而實例方法只有後面這種方式。也就是說,調用靜態方法能夠無需建立對象。
靜態方法在訪問本類的成員時,只容許訪問靜態成員(即靜態成員變量和靜態方法),而不容許訪問實例成員變量和實例方法;實例方法則無此限制。
靜態方法常被用於各類工具類、工廠方法類。
被 final
修飾的方法不能被子類覆寫(Override)。
final 方法示例:
public class FinalMethodDemo {
static class Father {
protected final void print() {
System.out.println("call Father print()");
};
}
static class Son extends Father {
@Override
protected void print() {
System.out.println("call print()");
}
}
public static void main(String[] args) {
Father demo = new Son();
demo.print();
}
}
// 編譯時會報錯
複製代碼
說明:
上面示例中,父類 Father 中定義了一個
final
方法print()
,則其子類不能 Override 這個 final 方法,不然會編譯報錯。
JDK8 開始,支持在接口 Interface
中定義 default
方法。default
方法只能出如今接口 Interface
中。
接口中被 default
修飾的方法被稱爲默認方法,實現此接口的類若是沒 Override 此方法,則直接繼承這個方法,再也不強制必須實現此方法。
default 方法語法的出現,是爲了既有的成千上萬的 Java 類庫的類增長新的功能, 且沒必要對這些類從新進行設計。 舉例來講,JDK8 中 Collection
類中有一個很是方便的 stream()
方法,就是被修飾爲 default
,Collection 的一大堆 List、Set 子類就直接繼承了這個方法 I,沒必要再爲每一個子類都注意添加這個方法。
default
方法示例:
public class DefaultMethodDemo {
interface MyInterface {
default void print() {
System.out.println("Hello World");
}
}
static class MyClass implements MyInterface {}
public static void main(String[] args) {
MyInterface obj = new MyClass();
obj.print();
}
}
// Output:
// Hello World
複製代碼
被 abstract
修飾的方法被稱爲抽象方法,方法不能有實體。抽象方法只能出現抽象類中。
抽象方法示例:
public class AbstractMethodDemo {
static abstract class AbstractClass {
abstract void print();
}
static class ConcreteClass extends AbstractClass {
@Override
void print() {
System.out.println("call print()");
}
}
public static void main(String[] args) {
AbstractClass demo = new ConcreteClass();
demo.print();
}
}
// Outpu:
// call print()
複製代碼
synchronized
用於併發編程。被 synchronized
修飾的方法在一個時刻,只容許一個線程執行。
在 Java 的同步容器(Vector、Stack、HashTable)中,你會見到大量的 synchronized 方法。不過,請記住:在 Java 併發編程中,synchronized 方法並非一個好的選擇,大多數狀況下,咱們會選擇更加輕量級的鎖 。
Java 中,有一些較爲特殊的方法,分別使用於特殊的場景。
Java 中的 main 方法是一種特殊的靜態方法,由於全部的 Java 程序都是由 public static void main(String[] args)
方法開始執行。
有不少新手雖然一直用 main 方法,殊不知道 main 方法中的 args 有什麼用。實際上,這是用來接收接收命令行輸入參數的。
示例:
public class MainMethodDemo {
public static void main(String[] args) {
for (String arg : args) {
System.out.println("arg = [" + arg + "]");
}
}
}
複製代碼
依次執行
javac MainMethodDemo.java
java MainMethodDemo A B C
複製代碼
控制檯會打印輸出參數:
arg = [A]
arg = [B]
arg = [C]
複製代碼
任何類都有構造方法,構造方法的做用就是在初始化類實例時,設置實例的狀態。
每一個類都有構造方法。若是沒有顯式地爲類定義任何構造方法,Java 編譯器將會爲該類提供一個默認構造方法。
在建立一個對象的時候,至少要調用一個構造方法。構造方法的名稱必須與類同名,一個類能夠有多個構造方法。
public class ConstructorMethodDemo {
static class Person {
private String name;
public Person(String name) {
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
public static void main(String[] args) {
Person person = new Person("jack");
System.out.println("person name is " + person.getName());
}
}
複製代碼
注意,構造方法除了使用 public,也可使用 private 修飾,這種狀況下,類沒法調用此構造方法去實例化對象,這經常用於設計模式中的單例模式。
JDK5 開始,Java 支持傳遞同類型的可變參數給一個方法。在方法聲明中,在指定參數類型後加一個省略號 ...
。一個方法中只能指定一個可變參數,它必須是方法的最後一個參數。任何普通的參數必須在它以前聲明。
變參方法示例:
public class VarargsDemo {
public static void method(String... params) {
System.out.println("params.length = " + params.length);
for (String param : params) {
System.out.println("params = [" + param + "]");
}
}
public static void main(String[] args) {
method("red");
method("red", "yellow");
method("red", "yellow", "blue");
}
}
// Output:
// params.length = 1
// params = [red]
// params.length = 2
// params = [red]
// params = [yellow]
// params.length = 3
// params = [red]
// params = [yellow]
// params = [blue]
複製代碼
finalize
在對象被垃圾收集器析構(回收)以前調用,用來清除回收對象。
finalize
是在 java.lang.Object
裏定義的,也就是說每個對象都有這麼個方法。這個方法在 GC 啓動,該對象被回收的時候被調用。
finalizer() 一般是不可預測的,也是很危險的,通常狀況下是沒必要要的。使用終結方法會致使行爲不穩定、下降性能,以及可移植性問題。
請記住:應該儘可能避免使用 finalizer()
。千萬不要把它當成是 C/C++ 中的析構函數來用。緣由是:Finalizer 線程會和咱們的主線程進行競爭,不過因爲它的優先級較低,獲取到的 CPU 時間較少,所以它永遠也趕不上主線程的步伐。因此最後可能會發生 OutOfMemoryError 異常。
擴展閱讀:
下面兩篇文章比較詳細的講述了 finalizer() 可能會形成的問題及緣由。
覆寫(Override)是指子類定義了與父類中同名的方法,可是在方法覆寫時必須考慮到訪問權限,子類覆寫的方法不能擁有比父類更加嚴格的訪問權限。
子類要覆寫的方法若是要訪問父類的方法,可使用 super
關鍵字。
覆寫示例:
public class MethodOverrideDemo {
static class Animal {
public void move() {
System.out.println("會動");
}
}
static class Dog extends Animal {
@Override
public void move() {
super.move();
System.out.println("會跑");
}
}
public static void main(String[] args) {
Animal dog = new Dog();
dog.move();
}
}
// Output:
// 會動
// 會跑
複製代碼
方法的重載(Overload)是指方法名稱相同,但參數的類型或參數的個數不一樣。經過傳遞參數的個數及類型的不一樣能夠完成不一樣功能的方法調用。
注意:
重載必定是方法的參數不徹底相同。若是方法的參數徹底相同,僅僅是返回值不一樣,Java 是沒法編譯經過的。
重載示例:
public class MethodOverloadDemo {
public static void add(int x, int y) {
System.out.println("x + y = " + (x + y));
}
public static void add(double x, double y) {
System.out.println("x + y = " + (x + y));
}
public static void main(String[] args) {
add(10, 20);
add(1.0, 2.0);
}
}
// Output:
// x + y = 30
// x + y = 3.0
複製代碼