1. 程式人生 > >java面向對象基礎筆記

java面向對象基礎筆記

系統設置 nor window下 多層 本質 字符串和編碼 操作 讀屬性 equal

Java面向對象編程

面向對象基礎

什麽是oop?

面向對象編程是一種對現實世界建立計算機模型的一種編程方法。簡稱OOP。OOP:Object Oriented Programming

對象的概念

在現實世界中,當我們提到動物這個概念,實際上它是一個抽象的概念。而具體動物是指老虎,獅子,大象等等。在對應的計算機模型中,我們把動物這種抽象的概念稱之為class,也就是類。而那些具體的對象稱之為實例,並且用不同變量標識不同的實例。

類和實例的關系

class是對象的模板。

  1. class定義了如何創建實例。
  2. class名字就是數據類型。

Instance是對象實例。

  1. Instance是根據class創建的實例。
  2. 可以創建多個instance
  3. 各個instance類型相同,但各自屬性各自不同

定義class

一個class可以包含多個field(字段)

field用來描述一個class的特征

class實現了數據封裝

創建實例

new操作符可以創建一個實例

定義一個引用類型變量來指向實例

通過變量來操作實例

通過變量.字段來訪問實例字段

例子

package com.day.one;

 

public class Main {

    public static void main(String[] args) {

       Person ps = new Person();

       ps.name 
= "小明"; ps.age = 12; System.out.println(ps.name); System.out.println(ps.age); } }

數據封裝

一個class可以包含多個filed

直接把filed用public暴露個外部可能破環了封裝

用private修飾field可以拒絕外部訪問

定義public方法可以間接修改filed

方法

public方法封裝了數據訪問

通過方法訪問了實例字段更安全

通過變量.方法名()來調用實例方法

方法的定義

修飾符

方法返回值

方法名字

方法參數列表

方法的返回值通過return語句實現

沒有返回值可以剩return

方法可以使用隱式變量this

this指向當前實例

this.filed 可以訪問當前實例的字段

在不引起歧義情況下,可以剩略this

局部變量名優先

調用方法

實例變量.方法名(參數)

可以忽略方法返回值

方法參數

用於接收傳遞給方法的變量值

分為 基本類型參數 引用類型參數

Private方法

外部代碼不可訪問private方法

內部代碼可以調用自己的private方法

例子

package com.day.one;

 

public class Main {

    public static void main(String[] args) {

       Person ps = new Person();

       ps.setName("小明");

       ps.setAge(12);

       System.out.println(ps.getName());

       System.out.println(ps.getAge());

       System.out.println(ps.getBrith());

    }

}

package com.day.one;

 

public class Person {

    private String name;

    private int age;

 

    public String getName() {

       return name;

    }

 

    public void setName(String name) {

       this.name = name.trim();

    }

 

    public int getAge() {

       return age;

    }

 

    public void setAge(int age) {

       this.age = age;

    }

 

    public int getBrith(){

       return this.calcBrith(2018);

    }

    private int calcBrith(int year) {

       return year - this.age;

    }

}

構造方法

構造方法可以在創建對象實例時初始化對象實例

構造方法名就是類名

構造方法的參數沒有限制

構造方法沒有返回值(也沒有void)

必須使用new操作符調用構造方法

默認構造方法

如果一個類沒有定義構造方法,編譯器會自動生成一個默認的構造方法:無參數,無執行語句

如果自定義了構造方法,編譯器就不再自動創建默認的構造方法

初始化順序

  1. 先初始化字段
  2. 沒有賦值的字段初始化為默認值:基本類型=0,引用類型=null,Boolean = false
  3. 再執行構造方法的代碼

可以定義多個構造方法,編譯器通過構造方法的參數數量,位置和類型區分

一個構造方法可以調用其他的構造方法,便於代碼復用,使用this()調用

例子

package com.day.one;

 

public class Main {

    public static void main(String[] args) {

       Person ps = new Person();

       Person ps1 = new Person("liming", 11);

       System.out.println(ps.getName());

       System.out.println(ps.getAge());

      

       System.out.println(ps1.getName());

   

    }

}

package com.day.one;

 

public class Person {

    private String name;

    private int age;

 

    public Person(String name, int age) {

       this.name = name;

       this.age = age;

    }

 

    public Person() {

       this("libai", 14);

    }

 

    public String getName() {

       return name;

    }

 

    public void setName(String name) {

       this.name = name.trim();

    }

 

    public int getAge() {

       return age;

    }

 

    public void setAge(int age) {

       this.age = age;

    }

 

    public int getBrith() {

       return this.calcBrith(2018);

    }

 

    private int calcBrith(int year) {

       return year - this.age;

    }

}

方法重載

方法重載是指:

多個方法的方法名相同

但各自的參數不同:

  1. 參數個數不同
  2. 參數類型不同
  3. 參數位置不同

方法返回值類型通常是相同

方法重載的目地:

l 相同功能的方法使用同一名字

l 便於調用

例子

package com.day.one;

 

public class Main {

    public static void main(String[] args) {

       Person ps = new Person();

      

       ps.setName("一個參數");

       System.out.println(ps.getName());

      

       ps.setName("第二", "參數");

       System.out.println(ps.getName());

   

    }

}

package com.day.one;

 

public class Person {

    private String name;

    private int age;

 

    public Person(String name, int age) {

       this.name = name;

       this.age = age;

    }

 

    public Person() {

       this("libai", 14);

    }

 

    public String getName() {

       return name;

    }

 

    public void setName(String name) {

       this.name = name.trim();

    }

 

    public void setName(String firstName, String lastName) {

       this.name = firstName + " | " + lastName;

    }

 

    public int getAge() {

       return age;

    }

 

    public void setAge(int age) {

       this.age = age;

    }

 

    public int getBrith() {

       return this.calcBrith(2018);

    }

 

    private int calcBrith(int year) {

       return year - this.age;

    }

}

繼承和多態

繼承

繼承是代碼復用的一種方式。

超類(super),父類,基類

子類(subclass),擴展類

繼承樹

Java默認所有類繼承Object

Java只允許一個類繼承自一個類

一個類有且僅有一個父類(Object除外)

Protected

父類定義的私有字段不允許被子類訪問

父類定義的所保護字段允許被子類訪

Protected把字段和方法的訪問權限控制繼承樹內部

super

supser關鍵字表示父類(超類)

構造方法的第一行語句必須調用super()

沒有super()時編譯器會自動生成super()

如果父類沒有定義默認的構造方法,有其他帶參數的構造方法,編譯器替子類生成的super()就調用不了,所以子類就必須顯示調用super(),傳入需要的參數。

向上轉型

當我們定義一個類的時候,實際上就是定義一種數據類型。當我們定義了繼承的時候,實際上就是在這些數據類型之間加上繼承的關系,當我們創建一個實例對象的時候,我們需要把它賦值一個引用類型變量,讓這個變量指向這個實例對象。但我們遇到一個問題:

可以對實例變量進行向上轉型(upcasting)

向上轉型把一個子類型安全地變為更加抽象的類型

向下轉型

可以對實例變量進行向下轉型(downcasting)

向下轉型把抽象的類型變成一個具體的子類型

向下轉型很可能會報錯:ClassCastException

Instanceof操作符可以判斷對象的類型

區分繼承和組合

Student 不宜從Book繼承

Student可以持有一個Book實例

繼承是is關系

組合是has關系

class Person() {}

class Book(){}

public class Student extends Person(

private Book book;

){}

例子

package com.day.one;

 

public class Main {

    public static void main(String[] args) {

 

       Person p = new Person("原始人");

       Student s = new Student("李白");

 

       s.readBood();

       p.readBood();

 

       s.foot = "大腳";

       System.out.println(s.foot);

      

       // upcasting

       Person ps = new Student("dufu");

       Object o = p;

      

      

       // downcasting

       // 向下轉型前可以用instanceof判斷

       if(p instanceof Student){

           Student s1 = (Student)p;

       }

      

      

      

       System.out.println(p instanceof Person);

       System.out.println(p instanceof Student);

      

 

    }

}

package com.day.one;

 

public class Student extends Person {

    public Student(String name) {

       super(name);

       // TODO Auto-generated constructor stub

    }

 

    private int scroe;

 

    public int getScroe() {

       return scroe;

    }

 

    public void setScroe(int scroe) {

       this.scroe = scroe;

    }

}

package com.day.one;

 

public class Person {

    private String name;

    private int age;

    protected String foot;

   

   

    public Person(String name){

       this.name = name;

    }

 

    public String getName() {

       return name;

    }

 

    public void setName(String name) {

       this.name = name.trim();

    }

 

    public void setName(String firstName, String lastName) {

       this.name = firstName + " | " + lastName;

    }

 

    public int getAge() {

       return age;

    }

 

    public void setAge(int age) {

       this.age = age;

    }

   

   

    public void readBood() {

       System.out.println(this.getName()+ "讀書");

    }

 

}

多態

Overide(覆寫)

子類覆寫父類的方法是覆寫(overide)

方法簽名如果不同就不是overide,而是創建一個新方法

(方法簽名由方法名稱和一個參數列表(方法的參數的順序和類型)組成。

註意,方法簽名不包括方法的返回類型。不包括返回值和訪問修飾符。)

可以使用註解@Overide讓編譯器檢測是否正確的覆寫

引用變量的聲明類型可能與實際類型不符。

實例對象的方法調用總是對應實際類型。

Java的實例方法調用是基於運行時實際類型的動態調用。

多態

public void runTwice(Person p){

p.run();

p.run();

}

從上面的代碼,無法知道是否調用Person類的定義的 run()方法,還是他的父類還是他的子類。

多態是指 針對 某個類型的方法調用,其真正執行的方法取決於運行時期實際類型的方法。

對某個類型調用某個方法,執行的方法可能是某個子類的覆寫方法。

利用多態,允許添加更多類型的子類實現功能擴展。

Object定義的幾個重要方法:

toString: 把instance輸出為String

equals:判斷連個instance是否邏輯相等

hasCode:計算一個instance的哈希值

final

用final修飾的方法不能被override

用final修飾的類不能被繼承

用final修飾的字段在初始化不能被修改

例子

package com.day.one;

 

public class Main {

    public static void main(String[] args) {

 

       Person p = new Person("原始人");

       Student s = new Student("李白");

       // Java的實例方法調用是基於運行時實際類型的動態調用。

      

   

       System.out.println(s.readBook());

       System.out.println(p.readBook());

       // 實際調用Object類的toString 打印出Person在jvm的引用地址

       System.out.println(p);

    }

}

package com.day.one;

 

public class Student extends Person {

    public Student(String name) {

       super(name);

       // TODO Auto-generated constructor stub

    }

 

    private int scroe;

 

    public int getScroe() {

       return scroe;

    }

 

    public void setScroe(int scroe) {

       this.scroe = scroe;

    }

 

    @Override

    public String readBook() {

       // super可以調用父類的被 重寫的方法

       return super.readBook() + "!!!";

    }

}

package com.day.one;

 

public class Person {

    private String name;

    private int age;

    protected String foot;

   

   

    public Person(String name){

       this.name = name;

    }

 

    public String getName() {

       return name;

    }

 

    public void setName(String name) {

       this.name = name.trim();

    }

 

    public void setName(String firstName, String lastName) {

       this.name = firstName + " | " + lastName;

    }

 

    public int getAge() {

       return age;

    }

 

    public void setAge(int age) {

       this.age = age;

    }

   

   

    public String readBook() {

       return this.name + "讀書";

    }

 

}

抽象類

覆寫

每個子類都可以覆寫父類的方法。如果父類的方法沒有實際意義,能否去掉方法的執行語句?

不能,如果去掉父類的方法,就會失去多態的特性,可以把父類聲明抽象類和父類方法聲明抽象方法。

抽象方法

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

抽象方法用abstract修飾

抽象方法沒有任何執行語句

因為無法執行抽象方法,因為這個類也必須申明為抽象類。

無法實例化一個抽象類

抽象類

抽象類用於被繼承

抽象類可以強迫子類實現其定義的抽象方法(否則編譯錯誤)

抽象方法實際上相當於定義了“規範”

如果不實現抽象方法,則該子類仍是抽象類。

面向抽象編程的本質:

上層代碼只定義規範

不需要子類就可以實現業務邏輯

具體的業務邏輯由不同子類實現,調用者並不關心

例子

package com.day.two;

 

public class Cirle extends Shape {

 

    private final double radius;

 

    public Cirle(double radius) {

       this.radius = radius;

    }

 

    @Override

    public double area() {

       // TODO Auto-generated method stub

       return Math.PI * radius * radius;

    }

 

}

package com.day.two;

 

public class Rect extends Shape {

 

    private final double width;

    private final double height;

 

    public Rect(double width, double height) {

       this.width = width;

       this.height = height;

    }

 

    @Override

    public double area() {

       // TODO Auto-generated method stub

       return this.height * this.width;

    }

 

}

package com.day.two;

 

public abstract class Shape {

    public abstract double area();

}

package com.day.two;

 

public class Main {

 

    public static void main(String[] args) {

       // TODO Auto-generated method stub

       Rect rect = new Rect(12,12);

       Cirle cirle = new Cirle(5);

       System.out.println(rect.area());

       System.out.println(cirle.area());

    }

 

}

接口

如果一個抽象類沒有字段,所有的方法都是抽象方法,就可以把抽象類改寫為接口。

使用interface聲明一個接口。

接口默認定義方法是public abstract

Interface是java內置的純對象接口。

實現interface使用implements

可以實現多個interface

註意區分術語

Java的接口特指interface定義的接口,只定義方法簽名

編程的接口泛指接口規範,如方法簽名,數據格式,網絡協議等

抽象類和接口的區別

abstract class

interface

繼承

單繼承

多接口

字段

可以定義實例字段

不可以定義實例字段

抽象方法

Yes

Yes

非抽象方法

可以定義非抽象方法

可以定義default方法

接口繼承

一個接口可以繼承另一個接口,使用extends, 相當於擴展的接口的方法

繼承

合理設計interface和abstract class的繼承關系

公共邏輯放在abstract class

接口層次代表抽象程度。

例子

package com.day.two;

 

public class Main {

 

    public static void main(String[] args) {

       // TODO Auto-generated method stub

       Rect rect = new Rect(12,12);

       Cirle cirle = new Cirle(5);

       System.out.println(rect.area());

       System.out.println(cirle.area());

    }

 

}

 

package com.day.two;

 

public class Cirle implements Shape {

 

    private final double radius;

 

    public Cirle(double radius) {

       this.radius = radius;

    }

 

    @Override

    public double area() {

       // TODO Auto-generated method stub

       return Math.PI * radius * radius;

    }

 

    @Override

    public String hehe() {

       // TODO Auto-generated method stub

       return null;

    }

 

}

package com.day.two;

 

public class Rect implements Shape {

 

    private final double width;

    private final double height;

 

    public Rect(double width, double height) {

       this.width = width;

       this.height = height;

    }

 

    @Override

    public double area() {

       // TODO Auto-generated method stub

       return this.height * this.width;

    }

 

    public String hehe() {

       return "rect";

    }

 

}

package com.day.two;

 

public interface Shape extends Shape2 {

    double area();

 

    default double zhouchang() {

       return 0;

    }

}

package com.day.two;

 

public interface Shape2 {

    String hehe();

}

包和classpath

靜態字段和方法

靜態字段

用static修飾的字段稱為靜態字段

普通字段在每個實例中都有自己一個獨立空間

靜態字段只有一個共享的空間,所有的實例都共享該字段

不推薦用實例變量訪問靜態字段

推薦用類名訪問靜態字段(因為在java程序中,實例對象並沒有靜態字段,代碼中實例對象能訪問靜態字段,是因為編譯器根據實例自動轉換類名來訪問靜態字段)

可以把靜態字段理解為描述class本身的字段(非實例字段)

靜態方法

用static修飾的方法稱為靜態方法

調用實例方法必須通過實例變量

調用靜態方法不需要實例變量

靜態方法類似其他編程語言的函數

靜態方法不能訪問this變量

靜態方法不能訪問實例字段

靜態方法可以訪問靜態字段和靜態方法

靜態方法通常用於工具類

Array.sort()

Math.random

靜態方法經常用於輔助方法

Java入口程序main()也是靜態方法

例子

package com.day.there;

 

public class Main {

    public static void main(String[] args) {

       Person p1 = new Person("li");

       System.out.println(Person.getNumber());

       Person p2 = new Person("li");

       System.out.println(Person.getNumber());

       Person p3 = new Person("li");

       System.out.println(Person.getNumber());

    }

}

package com.day.there;

 

public class Person {

    private String name;

    private static int num;

 

    public Person(String name) {

       this.name = name;

       Person.num++;

    }

   

    public static int getNumber(){

       return Person.num;

    }

}

Package

Java定義名字空間:包

包名+類名 = 完整類名

Jvm只看完整類名,只要包名不同,類就不同

包可以是多層結構

包沒有父子關系(java.util和java.util.zip是不同的包)

包作用域

位於同一個包的類,可以訪問包作用域的字段和方法:

不用public,private,protected修飾的字段和方法就是包作用域

引用其他類

先使用完整類名

先import,再使用類名

可以使用*(不推薦)

作用域

訪問權限

Java的類,字段,方法,接口都可以設置訪問權限

訪問權限是指在一個類的內部,能否引用另一個類以及訪問它的字段和方法

訪問權限有public,private,protected,package

Public

定義為public的class,interface,field,method可以被其他類訪問

Private

定義為private的field,method不可以被其他類訪問

Private訪問權限限定在class的內部,與方法生命順序無關

定義為private的class無法被其他類訪問

訪問private class 被限定在外層class的內部

定義在一個class內部的class稱為內部類

Protected

Protected作用於繼承關系

定義protected的字段和方法可以被子類訪問

Package

包作用域是指一個類允許訪問同一個package的

沒有public,private修飾的class

沒有public,protected,private修飾的字段和方法

包名必須完全一致

局部變量

在方法內部定義的變量稱為局部變量

局部變量作用域從變量聲明出開始到對應的塊結束

盡可能把局部變量的作用域縮小

盡可能延後生命局部變量

Final

Final與訪問權限不沖突

用final修飾class可以阻止被繼承

用final修飾method可以阻止被覆寫

用final修飾filed可以被重新賦值

用final修飾局部變量可以阻止被重新賦值

classpath和jar

classpath

classpath是一個環境變量

classpath指示jvm如何搜索class

classpath設置的搜索路徑與操作系統相關:window下是一分號分隔,linux是以冒號分隔

classpath設置方法:

在系統環境中設置(不推薦,在系統設置的classpath會幹擾到特定程序)

啟動JVM時用-classpath或-cp設置(推薦)

Eclipse自動傳入當前工程bin目錄作為classpath

Jar包

jar包是zip格式的壓縮文件,包含若幹class文件

jar包相當於目錄

使用jar包來避免大量目錄和class文件

創建jar包:

l JDK的jar命令

l Maven等工具

l 壓縮為zip然後改名為jar

Jar包其他功能

jar包可以有一個特殊的/META-INF/MANIFEST.MF文件來指定Main-Class

MANIFEST.MF是純文本,可以指定Main-class和其他信息

Jar包還可以包含其他jar包

JDK的class

Jvm運行時會自動加載jdk自帶的class

Jdk自帶的class被打包在rt.jar包

不需要在classpath中引用rt.jar包,jvm會自動加載

Java核心類

字符串和編碼

String的特點

可以直接使用”…”

內容不可變

比較String內容是否相等: equals(Object)

比較String 不區分大小寫: equalsgnoreCase(String)

是否包含子串:boolean contains(CharSequence)

返回子串的索引位置 int indexOf(String)

向後查找: int lastIndexOf(String)

查找字符串有某個位置開始:boolean startsWith(String)

查找字符串有莫個位置結束: Boolean endsWith(String)

trim()

移除首尾空白字符

空格, \t \r \n

註意:trim()不改變字符串內容,而是返回新的字符串

提取子串,沒有改變原字符串,返回新串 substring()

大小寫切換:toUpperCase() toLowerCase()

替換一個字符或一串字符: replace

使用正則替換:replaceAll(String, String)

分割字符串:String[] split(String)

拼接字符串:static String.join()

任意數據轉換string

static String.valueOf(int|Object|boolean)

把string轉換其他類型

static int Integer.parseInt(String)

static Integer Integer.valueOf(String)

String轉換為char[] char[] toCharArray()

Char[]轉換為String new String(char[])

String轉換為byte[] byte[] getBytes(“”) // 默認使用系統編碼,window默認編碼gbk,linux默認的utf8 不推薦使用

Byte[]轉換String

New String(byte[], String)

New String(byte[], Charset)

Java使用Unicode編碼

輸入輸出時把String和byte[]轉換,需要考慮編碼

始終優先考慮utf-8編碼

StringBuilder

String可以使用+拼接

每次循環都會創建新的字符串對象

絕大部分都是臨時對象,浪費內存

影響GC效率

StringBuilder可以高效拼接字符串

l StringBuilder是可變對象

l StringBuilder可以預分配緩沖區

StringBuilder可以進行鏈式操作

當只有一行代碼:不需要特別改寫字符串+操作

編譯器在內部自動把多個連續的+操作優化為StringBuilde操作

StringBuffer是StringBuilder的線程安全版本,很少使用

包裝類型

Jdk為每種基本類型都創建了對應的包裝類型

基本類型

對應的應用類型

boolean

Boolean

byte

Byte

short

Short

int

Integer

long

Long

float

Float

double

Double

char

character

int,Integer和String的相互轉換

int i = 100;

Integer n1 = new Integer(i);

Integer n2 = Integer.valueOf(i);

Integer n3 = Integer.valueOf(“100”);

int x1 = n1.intValue();

int x2 = Integer.parseInt(“100”);

特別註意Integer.getInteger(String)是從系統環境中讀取系統變量

自動裝箱和自動拆箱

發生在編譯階段

裝箱和拆箱會影響執行效率

編譯後的class代碼是嚴格區分基本類型和引用類型

Number

java包裝類型全部繼承Number 這個類

可以利用向上轉型變成Number對象,然後通過Number通過方法轉換為基本類型

JavaBean

l 若個字段private實例字段

l 通過public方法讀寫實例字段

符合命名規範的class被稱為JavaBean

通常把一組對應的getter和setter稱為屬性

只有getter的屬性成為只讀屬性

只有setter的屬性成為只寫屬性

屬性只需要定義getter/setter方法,不一定需要對應的字段

使用Introspector.getBeanInfo()獲取屬性列表

作用

方便ide工具讀取屬性

傳遞數據

枚舉屬性

枚舉類

用enum定義常量:

關鍵字enum定義常量類型

常量本身帶有類型信息

使用==比較

enum定義的類型實際上是class

繼承自java.lang.Enum

不能通過new創建實例

所有常量都是唯一的實例(引用類型)

可以用switch語句

java面向對象基礎筆記