創建類
定義類Bicycle:
public class Bicycle {
// the Bicycle class has
// three fields
public int cadence;
public int gear;
public int speed;
// the Bicycle class has
// one constructor
public Bicycle(int startCadence, int startSpeed, int startGear) {
gear = startGear;
cadence = startCadence;
speed = startSpeed;
}
// the Bicycle class has
// four methods
public void setCadence(int newValue) {
cadence = newValue;
}
public void setGear(int newValue) {
gear = newValue;
}
public void applyBrake(int decrement) {
speed -= decrement;
}
public void speedUp(int increment) {
speed += increment;
}
}
創建子類MountainBike繼承類Bicycle:
public class MountainBike extends Bicycle {
// the MountainBike subclass has
// one field
public int seatHeight;
// the MountainBike subclass has
// one constructor
public MountainBike(int startHeight, int startCadence,
int startSpeed, int startGear) {
super(startCadence, startSpeed, startGear);
seatHeight = startHeight;
}
// the MountainBike subclass has
// one method
public void setHeight(int newValue) {
seatHeight = newValue;
}
}
可以看出定義類的語法如下:
class MyClass {
// field, constructor, and
// method declarations
}
複雜點的:
class MyClass extends MySuperClass implements YourInterface {
// field, constructor, and
// method declarations
}
定義了類MyClass繼承類MySuperClass,並實現接口YourInterface。類只能有一個父類,但是能實現多個接口。
定義方法
方法定義示例:
public double calculateAnswer(double wingSpan, int numberOfEngines,
double length, double grossTons) {
//do the calculation here
}
方法簽名,方法名+參數類型,比如上面的方法簽名是:
calculateAnswer(double, int, double, double)
1個類中可以有多個同名的,但是不同參數的方法,也就是方法重載:
public class DataArtist {
...
public void draw(String s) {
...
}
public void draw(int i) {
...
}
public void draw(double f) {
...
}
public void draw(int i, double f) {
...
}
}
但是方法重載應謹慎使用,它會降低代碼可讀性。
類構造器
類有個默認的無參數的構造器,也可以自定義:
public Bicycle(int startCadence, int startSpeed, int startGear) {
gear = startGear;
cadence = startCadence;
speed = startSpeed;
}
構造器跟類同名,沒有return。類可以有多個不同參數列表的構造器。
調用
參數定義的叫做Parameters ,實際傳入的叫做Arguments 。
基本數據類型
public double computePayment(
double loanAmt,
double rate,
double futureValue,
int numPeriods) {
double interest = rate / 100.0;
double partial1 = Math.pow((1 + interest),
- numPeriods);
double denominator = (1 - partial1) / interest;
double answer = (-loanAmt / denominator)
- ((futureValue * partial1) / denominator);
return answer;
}
類對象
public Polygon polygonFrom(Point[] corners) {
// method body goes here
}
可變參數
public Polygon polygonFrom(Point... corners) {
int numberOfSides = corners.length;
double squareOfSide1, lengthOfSide1;
squareOfSide1 = (corners[1].x - corners[0].x)
* (corners[1].x - corners[0].x)
+ (corners[1].y - corners[0].y)
* (corners[1].y - corners[0].y);
lengthOfSide1 = Math.sqrt(squareOfSide1);
// more method body code follows that creates and returns a
// polygon connecting the Points
}
使用...
來表示可變參數。
方法中的基本數據類型,return後消失:
public class PassPrimitiveByValue {
public static void main(String[] args) {
int x = 3;
// invoke passMethod() with
// x as argument
passMethod(x);
// print x to see if its
// value has changed
System.out.println("After invoking passMethod, x = " + x);
}
// change parameter in passMethod()
public static void passMethod(int p) {
p = 10;
}
}
方法中的引用類型,return後還有作用到原來的對象:
public class RefType {
public void moveCircle(Circle circle, int deltaX, int deltaY) {
// code to move origin of circle to x+deltaX, y+deltaY
circle.setX(circle.getX() + deltaX);
circle.setY(circle.getY() + deltaY);
// code to assign a new reference to circle
circle = new Circle(0, 0);
}
public static void main(String[] args) {
Circle circle = new Circle(1, 1);
RefType refType = new RefType();
refType.moveCircle(circle, 2, 2);
System.out.println(circle.getX());
}
}
運行結果爲3。moveCircle裏面的circle,set方法調用會影響到引用的對象。雖然最後new了一個新對象,但是賦值給的是方法內部的這個circle,return後消失,外面的circle不受影響。可以這麼理解,外面和裏面的兩個circle,引用的都是同一個對象。
對象
創建對象:
Point originOne = new Point(23, 94);
Rectangle rectOne = new Rectangle(originOne, 100, 200);
Rectangle rectTwo = new Rectangle(50, 100);
變量聲明:
type name;
如果是基本數據類型,則會先分配內存空間。如果是類引用,則不會,在new的時候纔會分配內存空間。
字段:
objectReference.fieldName
方法:
objectReference.methodName(argumentList);
請記住,一個對象能有多個引用。
類的高級用法
方法退出有3種情況:
-
代碼執行完
-
throw異常
-
return
返回類型如果是類,那麼可以return子類。返回類型如果是接口,那麼可以return接口實現類。
this關鍵字表示當前類,或用來調用其他構造方法:
public class Rectangle {
private int x, y;
private int width, height;
public Rectangle() {
this(0, 0, 1, 1);
}
public Rectangle(int width, int height) {
this(0, 0, width, height);
}
public Rectangle(int x, int y, int width, int height) {
this.x = x;
this.y = y;
this.width = width;
this.height = height;
}
...
}
調用其他構造方法必須放在第一行。
範圍控制
top級別:public、package-private
member級別:public、private、protected、package-private
其中注意protected,等於package-private + 其他包中的子類(繼承了當前類,但是在其他包裏面)
怎麼理解?在自己的地盤(package)隨便玩,但是到了其他地盤,必須有父類保護(protected)
第3列的Subclass指的是其他包的子類。
訪問控制有2個用途:
-
在用別人的類時,看哪些可以使用
-
定義自己的類時,決定哪些允許別人用
static關鍵字,創建類字段:
public class Bicycle {
private int cadence;
private int gear;
private int speed;
private int id;
private static int numberOfBicycles = 0;
public Bicycle(int startCadence, int startSpeed, int startGear){
gear = startGear;
cadence = startCadence;
speed = startSpeed;
// increment number of Bicycles
// and assign ID number
id = ++numberOfBicycles;
}
// new method to return the ID instance variable
public int getID() {
return id;
}
...
}
可以直接用類名訪問:
Bicycle.numberOfBicycles
static關鍵字
創建類方法:
public static int getNumberOfBicycles() {
return numberOfBicycles;
}
創建常量:
static final double PI = 3.141592653589793;
注意,基本數據類型或string的常量,會在編譯的時候直接替換。如果依賴外部包的常量值變化了,比如PI變成了3.9,那麼當前代碼需要重新編譯。
關於static,有一個很重要的點是:static只能訪問static,也就是class級別只能訪問class級別,如果想訪問member級別,必須實例化對象後通過引用來訪問。
static塊
對於字段初始化來說,如果想寫多行代碼來初始化(比如寫個for循環來填充複雜數組),instance級別可以在構造函數來做,而對於class級別呢?就可以使用static塊:
public class MyClass {
public static int x;
public static int y;
static {
x = 10;
y = 20;
System.out.println("靜態塊執行");
}
public static void main(String[] args) {
System.out.println("x=" + x);
System.out.println("y=" + y);
}
}
static塊是用來給class級別字段做值初始化的,它們會按照在類中出現的順序依次執行,並且它們在構造函數之前執行。
也可以定義static方法後賦值:
class Whatever {
public static varType myVar = initializeClassVariable();
private static varType initializeClassVariable() {
// initialization code goes here
}
}
member級別字段初始化塊,也就是不帶static的:
{
// whatever code is needed for initialization goes here
}
會在每個constructor中執行。也可以使用final方法:
class Whatever {
private varType myVar = initializeInstanceVariable();
protected final varType initializeInstanceVariable() {
// initialization code goes here
}
}
爲什麼這裏必須要用final?因爲在實例初始化期間調用非final方法會報錯,這是爲了提高代碼健壯性、可讀性和可維護性,初始化本來就是一個確定的事情,那麼就用final限定清楚。
嵌套類
class OuterClass {
...
class InnerClass {
...
}
static class StaticNestedClass {
...
}
}
InnerClass能訪問OutClass所有成員(可以理解爲跟method類似),StaticNestedClass則不能。
什麼情況需要用嵌套類?
-
這個類只會被另外1個類使用,那麼可以定義爲嵌套類(helper classes),優化包結構
-
封裝,嵌套類可以訪問內部private成員
-
可讀性,小的嵌套類,方便閱讀
必須先實例化OuterClass再實例化InnerClass:
OuterClass outerObject = new OuterClass();
OuterClass.InnerClass innerObject = outerObject.new InnerClass();
而static嵌套類跟普通類一樣:
StaticNestedClass staticNestedObject = new StaticNestedClass();
注意,static嵌套類,跟其他頂層類是一樣的,意味着它不能直接訪問所在類的字段,而必須通過實例化對象引用才能訪問。(也許是爲了方便在一個文件裏面寫多個類,才設計了static嵌套類)
以下是示例:
OuterClass.java
public class OuterClass {
String outerField = "Outer field";
static String staticOuterField = "Static outer field";
class InnerClass {
void accessMembers() {
System.out.println(outerField);
System.out.println(staticOuterField);
}
}
static class StaticNestedClass {
void accessMembers(OuterClass outer) {
// Compiler error: Cannot make a static reference to the non-static
// field outerField
// System.out.println(outerField);
System.out.println(outer.outerField);
System.out.println(staticOuterField);
}
}
public static void main(String[] args) {
System.out.println("Inner class:");
System.out.println("------------");
OuterClass outerObject = new OuterClass();
OuterClass.InnerClass innerObject = outerObject.new InnerClass();
innerObject.accessMembers();
System.out.println("\nStatic nested class:");
System.out.println("--------------------");
StaticNestedClass staticNestedObject = new StaticNestedClass();
staticNestedObject.accessMembers(outerObject);
System.out.println("\nTop-level class:");
System.out.println("--------------------");
TopLevelClass topLevelObject = new TopLevelClass();
topLevelObject.accessMembers(outerObject);
}
}
TopLevelClass.java
public class TopLevelClass {
void accessMembers(OuterClass outer) {
// Compiler error: Cannot make a static reference to the non-static
// field OuterClass.outerField
// System.out.println(OuterClass.outerField);
System.out.println(outer.outerField);
System.out.println(OuterClass.staticOuterField);
}
}
一個變量引用的例子,這個例子展示了不同級別的同名變量,是如何取值的:
public class ShadowTest {
public int x = 0;
class FirstLevel {
public int x = 1;
void methodInFirstLevel(int x) {
System.out.println("x = " + x);
System.out.println("this.x = " + this.x);
System.out.println("ShadowTest.this.x = " + ShadowTest.this.x);
}
}
public static void main(String... args) {
ShadowTest st = new ShadowTest();
ShadowTest.FirstLevel fl = st.new FirstLevel();
fl.methodInFirstLevel(23);
}
}
x = 23
this.x = 1
ShadowTest.this.x = 0 // 注意這種在嵌套類取OuterClass同名字段的方式
Local Class,定義在method裏面的嵌套類:
public class LocalClassExample {
static String regularExpression = "[^0-9]";
public static void validatePhoneNumber(
String phoneNumber1, String phoneNumber2) {
final int numberLength = 10;
// Valid in JDK 8 and later:
// int numberLength = 10;
class PhoneNumber {
String formattedPhoneNumber = null;
PhoneNumber(String phoneNumber){
// numberLength = 7;
String currentNumber = phoneNumber.replaceAll(
regularExpression, "");
if (currentNumber.length() == numberLength)
formattedPhoneNumber = currentNumber;
else
formattedPhoneNumber = null;
}
public String getNumber() {
return formattedPhoneNumber;
}
// Valid in JDK 8 and later:
// public void printOriginalNumbers() {
// System.out.println("Original numbers are " + phoneNumber1 +
// " and " + phoneNumber2);
// }
}
PhoneNumber myNumber1 = new PhoneNumber(phoneNumber1);
PhoneNumber myNumber2 = new PhoneNumber(phoneNumber2);
// Valid in JDK 8 and later:
// myNumber1.printOriginalNumbers();
if (myNumber1.getNumber() == null)
System.out.println("First number is invalid");
else
System.out.println("First number is " + myNumber1.getNumber());
if (myNumber2.getNumber() == null)
System.out.println("Second number is invalid");
else
System.out.println("Second number is " + myNumber2.getNumber());
}
public static void main(String... args) {
validatePhoneNumber("123-456-7890", "456-7890");
}
}
Local Class只能訪問方法裏面的final變量,或者“看似final”的變量(值不會發生變化)。否則會報錯:local variables referenced from an inner class must be final or effectively final。但是能直接方法參數列表的parameters。
Anonymous Class,定義在method裏面的沒有名字的嵌套類:
public class HelloWorldAnonymousClasses {
interface HelloWorld {
public void greet();
public void greetSomeone(String someone);
}
public void sayHello() {
class EnglishGreeting implements HelloWorld {
String name = "world";
public void greet() {
greetSomeone("world");
}
public void greetSomeone(String someone) {
name = someone;
System.out.println("Hello " + name);
}
}
HelloWorld englishGreeting = new EnglishGreeting();
HelloWorld frenchGreeting = new HelloWorld() { // 匿名類
String name = "tout le monde";
public void greet() {
greetSomeone("tout le monde");
}
public void greetSomeone(String someone) {
name = someone;
System.out.println("Salut " + name);
}
};
HelloWorld spanishGreeting = new HelloWorld() { // 匿名類
String name = "mundo";
public void greet() {
greetSomeone("mundo");
}
public void greetSomeone(String someone) {
name = someone;
System.out.println("Hola, " + name);
}
};
englishGreeting.greet();
frenchGreeting.greetSomeone("Fred");
spanishGreeting.greet();
}
public static void main(String... args) {
HelloWorldAnonymousClasses myApp =
new HelloWorldAnonymousClasses();
myApp.sayHello();
}
}
它的語法跟constructor類似,new後面跟上implements接口或extends類的名字,但它是一個表達式,所以最後要加上分號。
請記住,Local Class和Anonymous Class,都是應以在method裏面的。
參考資料:
Classes and Objects https://dev.java/learn/classes-objects/