Java語法彙總-面向對象篇

Java面向對象語法彙總

接着上一篇《Java語法彙總-基礎語法篇》,來學習彙總Java的面向對象相關語法,總體感覺跟C#的語法差不多,基本上理念都是相通的,而且兩門語言近年來相互借鑑的也比較頻繁,只要掌握一門,那麼學習另一門也是分分鐘的事。

1. 基本類的定義

// 定義Person類
class Person {
    // 定義字段
    private String name;
    private int age;

    // 無參默認構造方法
    public Person() {
    }
    // 有參構造方法
    public Person(String name, int age) {
        this.name = name;
        this.age = age;
    }
    // 調用另一個構造方法Person(String, int)
    public Person(String name) {
        this(name, 18);
    }

    // 屬性賦值方法
    public String getName() {
        return this.name;
    }
    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return this.age;
    }
    public void setAge(int age) {
        if (age < 0 || age > 100) {
            throw new IllegalArgumentException("invalid age value");
        }
        this.age = age;
    }

    // 方法重載
    public void hello() {
        System.out.println("Hello, world!");
    }

    public void hello(String name) {
        System.out.println("Hello, " + name + "!");
    }

    public void hello(String name, int age) {
        if (age < 18) {
            System.out.println("Hi, " + name + "!");
        } else {
            System.out.println("Hello, " + name + "!");
        }
    }
}

2. 繼承與轉型

  • 子類繼承父類
class Student extends Person {

    private int score;

    public Student(String name, int age, int score) {
        super(name, age); // 調用父類的構造方法Person(String, int)
        this.score = score;
    }

    public int getScore() {
        return this.score;
    }
    public void setScore(int score) {
        this.score=score;
    }
}
  • 父子類轉型

Student s = new Student();
System.out.println(s instanceof Person); // true
System.out.println(s instanceof Student); // true

Person p = new Student();
if (p instanceof Student) {
    // 只有判斷成功纔會向下轉型:
    Student s = (Student) p; // 一定會成功
}

3. Override與多態

  • 在繼承關係中,子類如果定義了一個與父類方法簽名完全相同的方法,被稱爲覆寫(Override)。
  • 多態是指,針對某個類型的方法調用,其真正執行的方法取決於運行時期實際類型的方法。
class Person {
    public void run() {
        System.out.println("Person.run");
    }
}

class Student extends Person {
	// 加上@Override可以讓編譯器幫助檢查是否進行了正確的覆寫。
	// 但是@Override不是必需的。
    @Override
    public void run() {
        System.out.println("Student.run");
    }
}
public class Main {
    public static void main(String[] args) {
        Person p = new Student();
        p.run(); // 打印Student.run
    }
}

  • 多態綜合應用實例:
// 收入
class Income {
    protected double income;

    public Income(double income) {
        this.income = income;
    }
	// 應繳稅款
    public double getTax() {
        return income * 0.1; // 稅率10%
    }
}
//薪水
class Salary extends Income {
    public Salary(double income) {
        super(income);
    }

    @Override
    public double getTax() {
        if (income <= 5000) {
            return 0;
        }
        return (income - 5000) * 0.2;
    }
}
// 津貼補助
class SpecialAllowance extends Income {
    public StateCouncilSpecialAllowance(double income) {
        super(income);
    }
    @Override
    public double getTax() {
        return 0;
    }
}

public class Main {
    public static void main(String[] args) {
        // 給一個有普通收入、工資收入和享受特殊津貼的小夥伴算稅:
        Income[] incomes = new Income[] {
            new Income(3000),
            new Salary(7500),
            new SpecialAllowance(15000)
        };
        System.out.println(totalTax(incomes));
    }

    public static double totalTax(Income... incomes) {
        double total = 0;
        for (Income income: incomes) {
            total = total + income.getTax();
        }
        return total;
    }
}
  • 重寫Object方法

Object定義了幾個重要的方法:

  1. toString():把instance輸出爲String;
  2. equals():判斷兩個instance是否邏輯相等;
  3. hashCode():計算一個instance的哈希值。
class Person {
    ...
    // 顯示更有意義的字符串:
    @Override
    public String toString() {
        return "Person:name=" + name;
    }

    // 比較是否相等:
    @Override
    public boolean equals(Object o) {
        // 當且僅當o爲Person類型:
        if (o instanceof Person) {
            Person p = (Person) o;
            // 並且name字段相同時,返回true:
            return this.name.equals(p.name);
        }
        return false;
    }

    // 計算hash:
    @Override
    public int hashCode() {
        return this.name.hashCode();
    }
}
  • 密封關鍵字 final

如不允許被重寫,則添加final關鍵字

  1. final修飾的方法可以阻止被覆寫;
  2. final修飾的class可以阻止被繼承;
  3. final修飾的field必須在創建對象時初始化,隨後不可修改。
class Person {
    protected String name;
    public final String hello() {
        return "Hello, " + name;
    }
}

final class Person {
    protected String name;
}

class Person {
    public final String name = "Unamed";
}

class Person {
    public final String name;
    public Person(String name) {
        this.name = name;
    }
}

4. 抽象類和接口

  • 抽象類

如果一個class定義了方法,但沒有具體執行代碼,這個方法就是抽象方法,抽象方法用abstract修飾。

abstract class Person {
    public abstract void run();
}
class Student extends Person {
    @Override
    public void run() {
        System.out.println("Student.run");
    }
}

public class Main {
    public static void main(String[] args) {
        Person p = new Student();
        p.run();
    }
}

  • 接口

接口interface不能有連字段,接口定義的所有方法默認都是public abstract的。一個類可以實現多個接口。

  • 基礎定義
// 定義接口Person 
interface Person {
    void run();
    String getName();
}
// 實現接口
class Student implements Person {
    private String name;

    public Student(String name) {
        this.name = name;
    }

    @Override
    public void run() {
        System.out.println(this.name + " run");
    }

    @Override
    public String getName() {
        return this.name;
    }
}
// 一個類可以實現多個接口
class Student implements Person, Hello { // 實現了兩個interface
    ...
}

interface Hello {
    void hello();
}
// 接口繼承接口
interface Person extends Hello {
    void run();
    String getName();
}
  • default方法

實現類可以不必覆寫default方法。新增default方法時,子類不必全部修改,只需要在需要覆寫的地方去覆寫新增方法。
default方法和抽象類的普通方法是有所不同的。因爲interface沒有字段,default方法無法訪問字段,而抽象類的普通方法可以訪問實例字段。

interface Person {
    String getName();
    default void run() {
        System.out.println(getName() + " run");
    }
}

class Student implements Person {
    private String name;
    public Student(String name) {
        this.name = name;
    }
    public String getName() {
        return this.name;
    }
}
public class Main {
    public static void main(String[] args) {
        Person p = new Student("Xiao Ming");
        p.run();
    }
}

5. 靜態字段和靜態方法


class Person {
    public static int number;
    public static void setNumber(int value) {
        number = value;
    }
}

public class Main {
    public static void main(String[] args) {
        Person.setNumber(99);
        System.out.println(Person.number);
    }
}

public interface Person {
    // 編譯器會自動加上public statc final:
    int MALE = 1;
    int FEMALE = 2;
}

6. 作用域

  • Java內建的訪問權限包括public、protected、private和package權限;
  • Java在方法內部定義的變量是局部變量,局部變量的作用域從變量聲明開始,到一個塊結束;
  • final修飾符不是訪問權限,它可以修飾class、field和method;
  • 一個.java文件只能包含一個public類,但可以包含多個非public類。

7. 異常的使用

  • 捕獲異常
public static void main(String[] args) {
    try {
        process1();
        process2();
        process3();
    } catch (UnsupportedEncodingException e) {
        System.out.println("Bad encoding");
    } catch (IOException | NumberFormatException e) {
        System.out.println("IO error");
    } finally {
        System.out.println("END");
    }
}

  • 拋出異常
static void process1() {
     try {
         process2();
     } catch (NullPointerException e) {
         throw new IllegalArgumentException(e);
     }
 }

 static void process2() {
     throw new NullPointerException();
 }

 // 方法如果不處理異常,就先拋出來
 static byte[] toGBK(String s) throws UnsupportedEncodingException {
     // 用指定編碼轉換String爲byte[]:
     return s.getBytes("GBK");
 }
    
 public static void main( String[] args )
 {
     try {
         process1();
         byte[] bs = toGBK("中文");
        System.out.println(Arrays.toString(bs));
     } catch (Exception e) {
         e.printStackTrace();
     }
     System.out.println( "Hello World!" );
 }

  • 自定義異常
    一個常見的做法是自定義一個BaseException作爲“根異常”,然後,派生出各種業務類型的異常。
    BaseException需要從一個適合的Exception派生,通常建議從RuntimeException派生,其他業務類型的異常就可以從BaseException派生。
public class BaseException extends RuntimeException {
}
public class UserNotFoundException extends BaseException {
}
public class LoginFailedException extends BaseException {
}
// 自定義的BaseException應該提供多個構造方法
public class BaseException extends RuntimeException {

    public BaseException() {
        super();
    }

    public BaseException(String message, Throwable cause) {
        super(message, cause);
    }

    public BaseException(String message) {
        super(message);
    }

    public BaseException(Throwable cause) {
        super(cause);
    }
}
  • 空指針異常處理

如果調用方一定要根據null判斷,比如返回null表示文件不存在,那麼考慮返回Optional,這樣調用方必須通過Optional.isPresent()判斷是否有結果。

public Optional<String> readFromFile(String file) {
    if (!fileExist(file)) {
        return Optional.empty();
    }
    ...
}

8. 反射的使用

反射就是Reflection,Java的反射是指程序在運行期可以拿到一個對象的所有信息。

  • 獲取 Class
// 直接通過一個class的靜態變量class獲取
Class cls = String.class;
// 可以通過該實例變量提供的getClass()方法獲取
String s = "Hello";
Class cls = s.getClass();
// 可以通過靜態方法Class.forName()獲取
Class cls = Class.forName("java.lang.String");

static void printClassInfo(Class cls) {
    System.out.println("Class name: " + cls.getName());
    System.out.println("Simple name: " + cls.getSimpleName());
    if (cls.getPackage() != null) {
        System.out.println("Package name: " + cls.getPackage().getName());
    }
    System.out.println("is interface: " + cls.isInterface());
    System.out.println("is enum: " + cls.isEnum());
    System.out.println("is array: " + cls.isArray());
    System.out.println("is primitive: " + cls.isPrimitive());
}
public static void main(String[] args) {
    printClassInfo("".getClass());
    printClassInfo(Runnable.class);
    printClassInfo(java.time.Month.class);
    printClassInfo(String[].class);
    printClassInfo(int.class);
}
  • 獲取字段
  1. getName():返回字段名稱,例如,“name”;
  2. getType():返回字段類型,也是一個Class實例,例如,String.class;
  3. getModifiers():返回字段的修飾符
class Student extends Person {
    public int score;
    private int grade;
}

class Person {
    public String name;
    public Person(String name) {
        this.name = name;
    }
}

public class Main {
    public static void main(String[] args) throws Exception {
        Class stdClass = Student.class;
        // 獲取public字段"score":
        System.out.println(stdClass.getField("score"));
        // 獲取繼承的public字段"name":
        System.out.println(stdClass.getField("name"));
        // 獲取private字段"grade":
        System.out.println(stdClass.getDeclaredField("grade"));
        
        Object p = new Person("Xiao Ming");
        Class c = p.getClass();
        Field f = c.getDeclaredField("name");
        // 強制允許訪問
        f.setAccessible(true);
        // 獲取字段的值
        Object value = f.get(p);
        System.out.println(value); // "Xiao Ming"
    }
}

9. 註解的使用

註解是放在Java源碼的類、方法、字段、參數前的一種特殊“註釋”,註釋會被編譯器直接忽略,註解則可以被編譯器打包進入class文件,因此,註解是一種用作標註的“元數據”。

  • 定義註解
@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.FIELD)
public @interface Range {
    int min() default 0;
    int max() default 255;
}
  • 實現註解
void check(Person person) throws IllegalArgumentException, ReflectiveOperationException {
    // 遍歷所有Field:
    for (Field field : person.getClass().getFields()) {
        // 獲取Field定義的@Range:
        Range range = field.getAnnotation(Range.class);
        // 如果@Range存在:
        if (range != null) {
            // 獲取Field的值:
            Object value = field.get(person);
            // 如果值是String:
            if (value instanceof String) {
                String s = (String) value;
                // 判斷值是否滿足@Range的min/max:
                if (s.length() < range.min() || s.length() > range.max()) {
                    throw new IllegalArgumentException("Invalid field: " + field.getName());
                }
            }
        }
    }
}
  • 使用註解
public class Person {
    @Range(min=1, max=20)
    public String name;

    @Range(max=10)
    public String city;
}

public static void main( String[] args )
{
    Person p =new Person();
    p.city="23456789101";
    try {
        check(p);
    }catch (Exception e)
    {
        System.out.println(e.getMessage());
    }

    System.out.println( "Hello World!" );
}
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章