Java學習筆記-面向物件
面向物件部分
- Java的核心思想就是OOP
- 面向過程思想:步驟清晰簡單,面向過程適合處理一些較為簡單的問題。
- 面向物件思想:分類的思維模式,面向物件適合處理一些複雜的問題,適合處理需要多人協作的問題。
Java面向物件01:什麼是面向物件
- 面向物件程式設計(Object-Oriented Programming , OOP)
- 面向物件的本質就是:以類的方式組織程式碼,以物件的方式組織(封裝)資料。
- 抽象
- 三大特性:封裝、繼承、多型。
- 從認識論的角度考慮是先有物件後有類。
- 從程式碼執行的角度是先有類後有物件。
Java面向物件02:回顧方法的定義和呼叫
- 方法的定義:
- 修飾符
- 返回型別
- break和return的區別
- 方法名
- 引數列表
- 異常丟擲
- 方法的呼叫:
- 靜態方法
- 非靜態方法
- 形參和實參
- 值傳遞和引用傳遞
- this關鍵字
靜態與非靜態,程式碼如下:
package com.oop; public class Demo01 { //Main 方法 public static void main(String[] args) { // Student.say(); Student student = new Student(); student.say(); } //和類一起載入的 public static void a(){ b(); } //類例項化之後才存在 public void b(){ a(); } }
package com.oop;
public class Student {
// public static void say(){
// System.out.println("學生說話了");
// }
public void say(){
System.out.println("學生說話了");
}
}
實參和形參,程式碼如下:
package com.oop; public class Demo03 { public static void main(String[] args) { //實際引數和形式引數的名字要一一對應 System.out.println(add(1, 2)); } public static int add(int a,int b){ return a + b; } }
值傳遞,程式碼如下:
package com.oop;
//值傳遞
//以下的兩種change方法都是值傳遞
public class Demo04 {
public static void main(String[] args) {
int a = 1;
System.out.println(a);
Demo04.change01(a);
// Demo04.change02(a);
System.out.println(a);
}
// private static void change02(int a) {
// a = 10;
// return a;
// }
private static int change01(int a) {
a = 10;
return a;
}
}
引用傳遞(和值傳遞),程式碼如下:
package com.oop;
//引用傳遞:物件,本質還是值傳遞
public class Demo05 {
public static void main(String[] args) {
Person person = new Person();
System.out.println(person.name);
Demo05.change(person);
System.out.println(person.name);
}
public static void change(Person person){//因為是引用型別,都是對同一個記憶體地址的內容進行修改,所以本質還是值傳遞
person.name = "maynerd";
}
}
//定義了一個Person類,有一個屬性:name
class Person{
String name;
}
Java面向物件03:回顧方法的呼叫
Java面向物件04:類與物件的建立
使用new關鍵字建立物件
使用new關鍵字建立的時候,除了分配記憶體空間以外,還會給 建立好的物件 進行預設的初始化以及 對類中構造器的呼叫。
類中構造器也稱為構造方法,是在進行建立物件的時候必須要呼叫的。並且構造器有以下兩個特點:
- 必須和類的名字相同。
- 必須沒有返回型別,也不能寫void。
構造器必須掌握。
tip:好習慣:不要在每一個類裡面加上main方法,這樣是不好的,因為類裡面不應該有main方法,他就是一個單純的類,而一個程式只有一個主啟動類,所以我們可以定義一個大的主啟動類或者測試類。所以我們建造一個Application類。
程式碼如下:
package com.oop.Demo02;
//一個類應該只有一個main方法
public class Application {
public static void main(String[] args) {
Student xiaoming = new Student();
Student xiaohong = new Student();
xiaoming.name = "小明";
xiaoming.age = 3;
//預設初始化
System.out.println(xiaoming.name);
System.out.println(xiaoming.age);
xiaohong.name = "小紅";
xiaohong.age = 3;
System.out.println(xiaohong.name);
System.out.println(xiaohong.age);
}
}
package com.oop.Demo02;
//學生類
public class Student {
//屬性:欄位
String name;
int age;
//方法
public void study(){
System.out.println(this.name + "在學習");
}
}
Java面向物件05:構造器詳解
檢視一下class檔案,為什麼類裡邊是空的也能new?
IDEA看class檔案步驟:點開專案結構——>點開modules——>把out檔案加入進來——>點選OK再Apply。
一個類即使什麼都不寫,它也會存在一個方法。
可以顯式的定義構造器。
tip:一旦定義有參構造,無參構造就必須顯式定義
alt+insert生成構造器,生成有參/無參構造器
new物件的過程和有參無參構造,程式碼如下:
package com.oop.Demo02;
public class Person {
//一個類即使什麼都不寫,他也會存在一個方法
//顯式的定義一個構造器
String name;
int age;
//例項化初始值
//無參構造
//1.使用new關鍵字,本質是在呼叫構造器
//2.用來初始化值
public Person(){
this.name = "maynerd";
}
//有參構造:一旦定義有參構造,無參構造就必須顯式定義
public person(String name){
this.name = name;
}
//快捷鍵構造alt+insert
public Person(int age) {
this.age = age;
}
}
package com.oop.Demo02;
//一個類應該只有一個main方法
public class Application {
public static void main(String[] args) {
//new例項化了一個物件
Person person = new Person();
System.out.println(person.name);//meynerd
}
}
Java面向物件06:建立物件記憶體分析
建立物件程式碼:
package com.oop.Demo03;
public class Pet {
public String name;
public int age;
public void shout(){
System.out.println("叫了一聲");
}
}
package com.oop.Demo03;
public class Application {
public static void main(String[] args) {
Pet dog = new Pet();
dog.name = "旺財";
dog.age = 3;
dog.shout();
System.out.println(dog.name);
System.out.println(dog.age);
Pet cat = new Pet();
}
}
分析圖:
https://www.notion.so/791a81450ab644faa4bbe924a0db566e#18c4b8d8670c48e98213219e8a1c47f7
參考陣列部分:三種初始化及記憶體分析
Java面向物件07:簡單小結:類與物件
Java面向物件08:封裝詳解
高內聚,低耦合:高內聚就是類的內部資料操作細節自己完成,不允許外部干涉;低耦合就是僅暴露少量方法給外部使用。
封裝(資料的隱藏):通常應禁止直接訪問一個物件中資料的實際表示,而應通過操作介面來訪問,這稱為資訊隱藏。
tip:
- 屬性私有,get/set。
- alt+insert快捷鍵生成get/set方法
- 封裝:
- 提高程式的安全性,保護資料
- 隱藏程式碼實現細節
- 統一介面
- 系統可維護性增加
package com.oop.Demo04;
public class Student {
//屬性私有
private String name;
private int id;
private char sex;
private int age;
/*
提供一些可以操作這個屬性的方法
提供一些public的get/set方法
*/
//get獲得這個資料
public String getName() {
return name;
}
//set給這個資料設定值
public void setName(String name) {
this.name = name;
}
//alt+insert快捷鍵生成get/set方法
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public char getSex() {
return sex;
}
public void setSex(char sex) {
this.sex = sex;
}
public int getAge() {
return age;
}
public void setAge(int age) {
if(age > 120 || age < 0) {
System.out.println("年齡不合法!已自動設定為3歲!");
this.age = 3;
}
else this.age = age;
}
}
package com.oop.Demo04;
/*
1.提高程式的安全性,保護資料
2.隱藏程式碼實現細節
3.統一介面
4.系統可維護性增加
*/
public class Application {
public static void main(String[] args) {
Student s1 = new Student();
s1.setName("maynerd");
System.out.println(s1.getName());
s1.setAge(130);//不合法資料
s1.setAge(-1);//不合法資料
System.out.println(s1.getAge());
}
}
Java面向物件09:什麼是繼承
-
繼承的本質是對某一批類的抽象,從而實現對現實世界更好的建模。
-
Java類中只有單繼承沒有多繼承!
-
繼承是類和類之間的一種關係。除此以外,類和類之間的關係還有依賴、組合、聚合等。
-
繼承關係的兩個類,一個為子類(派生類)一個為父類(基類)。子類繼承父類,使用關鍵字extends來表示。
- Object類
- super-this
- 方法重寫
tip:ctrl+H開啟繼承樹。
Java中所有的類都預設直接或者間接繼承Object類。
Object類,程式碼如下:
package com.oop.Demo05;
public class Application {
public static void main(String[] args) {
Student student = new Student();
student.say();
System.out.println(student.money);
// Person person = new Person();
// person.
}
}
package com.oop.Demo05;
/*
基類
*/
public class Person /* extends Object */ {
public int money = 100000;
// private int money = 100000;//私有的是無法繼承的
public void say(){
System.out.println("說了一句話");
}
public int getMoney() {
return money;
}
public void setMoney(int money) {
this.money = money;
}
}
package com.oop.Demo05;
/*
派生類
*/
public class Student extends Person{
}
package com.oop.Demo05;
/*
派生類
*/
public class Teacher extends Person{
}
繼承樹,如圖:
Java面向物件10:super詳解
父類子類屬性的關係測試,程式碼如下:
父類子類方法測試,程式碼如下:
父類子類構造器測試,程式碼如下:
tip:
- super注意點:
- super呼叫父類的構造方法,必須在構造方法的第一個。
- super必須只能出現在子類的方法或者構造方法中!
- super和this不嫩惡搞同時呼叫構造方法!
- super 跟this 的區別:
-
代表物件不同:
this:本身呼叫者這個物件
super:代表父類物件的引用
-
前提:
this:沒有繼承也可以使用
super:只能在繼承條件下才可以使用
-
構造方法:
this():本類的構造
super():父類的構造
-
程式碼如下:
package com.oop.Demo05;
public class Application {
public static void main(String[] args) {
Student student = new Student();
student.test("lm");
System.out.println("==============");
student.test02();
}
}
package com.oop.Demo05;
/*
派生類
*/
public class Student extends Person02{
//構造器測試
public Student() {
//隱藏程式碼:先呼叫了父類的無參構造
//super();//且必須放在下面語句的上面,不可調換位置
System.out.println("Student(子類)無參構造器執行了!");
}
//屬性測試
private String name = "lianming";
public void test(String name){
System.out.println(name);//傳進來的lm
System.out.println(this.name);//本類的lianming
System.out.println(super.name);//父類的maynerd
}
//方法測試
public void test02(){
print();//Student
this.print();//Student
super.print();//Person02
}
public void print(){
System.out.println("Student");
}
}
package com.oop.Demo05;
/*
基類
*/
public class Person02 /* extends Object */ {
//構造器測試
public Person02() {
System.out.println("Person(父類)無參構造器執行了!");
}
//屬性測試
protected String name = "maynerd";
//方法測試
//私有的方法或者變數無法被繼承
public void print(){
System.out.println("Person");
}
}
Java面向物件11:方法重寫
package com.oop.Demo055;
public class Application {
public static void main(String[] args) {
A a = new A();
a.test();//A
//父類的引用指向子類
//方法呼叫只和左邊,定義的資料型別有關
B b = new A();
b.test();//B
}
}
package com.oop.Demo055;
public class A extends B{
public static void test(){
System.out.println("A的test()");
}
}
package com.oop.Demo055;
//重寫都是方法的重寫,和屬性無關
public class B {
public static void test(){
System.out.println("B的test()");
}
}
把關鍵字static刪掉會出現如圖示記嗎,表明方法重寫:
把A中的方法test刪了,alt+insert重寫該方法:
再點選執行,可以發現,都是B的test。可見靜態方法和非靜態方法的區別:
tip:
重寫:需要有繼承關係,子類重寫父類的方法!
- 方法名必須相同
- 引數列表必須相同
- 修飾符:範圍可以擴大但不能縮小 Public>Protected>Default>Private
- 丟擲的異常:範圍可以被縮小,但不能擴大 ClassNotFoundException →Exception
重寫,子類的方法和父類必須一致,方法體不同。
為什麼要重寫?父類的功能,子類不一定需要或者不一定滿足。
Java面向物件12:什麼是多型
- 即同一方法可以根據傳送物件的不同而採取多種不同的行為方式。
- 一個物件的實際型別是確定的,但可以指向物件的引用的型別有很多。
- 多型存在條件:
- 有繼承關係
- 子類重寫父類方法
- 父類引用指向子類物件
- 注意:多型是方法的多型,屬性沒有多型。
- instanceof
- 雖然new的是Student,但是用的是父類的方法,因為子類繼承父類。
- 子類重寫一下父類方法,輸出,可以發現,子類物件s1照常輸出自己方法,輸出son,父類s2因為方法重寫也輸出son。
- 子類增加eat,發現父類無法使用,只能強制轉換後可以使用。
package com.oop.Demo06;
public class Application {
public static void main(String[] args) {
//一個物件的實際型別是確定的
//new Strdent();
//mew Person();
//可以指向的引用型別就不確定了:父類引用指向子類
//Student 能呼叫的方式都是自己的或者繼承父類的
Student s1 = new Student();
//Person 父類,可以指向子類,但是不能呼叫子類獨有的方法
Person s2 = new Student();
Object s3 = new Student();
s2.run();//子類重寫了父類方法,執行子類方法
s1.run();
((Student)s2).eat();//只能強制型別轉換才能執行
}
}
package com.oop.Demo06;
public class Person {
public void run(){
System.out.println("run");
}
}
package com.oop.Demo06;
public class Student extends Person {
@Override
public void run() {
System.out.println("son");
}
public void eat(){
System.out.println("eat");
}
}
多型注意事項:
- 多型是方法的多型,屬性沒有多型。
- 父類和子類,有繼承關係,才能進行強制型別轉換。型別轉換異常ClassCastException!
- 存在條件:繼承關係,方法需要重寫,父類引用指向子類物件。Father f1 = new Son();
- 哪些方法不能重寫?
- static 方法,屬於類,它不屬於例項
- final常量
- private方法
Java面向物件13:instaceof和型別轉換
A instanceof B用來判斷A是否與B(或者B的左邊定義的引用資料型別)有直接或者間接的繼承關係。
總結:有關係的才能編譯通過,毫無關係的,不能編譯通過。有父子關係的是true,沒有父子關係的是false
程式碼如下:
package com.oop.Demo06;
public class Application02 {
public static void main(String[] args) {
// Object > String
// Object > Person > Teacher
// Object > Person > Student
Object object = new Student();
System.out.println(object instanceof Student);//true
System.out.println(object instanceof Person);//true
System.out.println(object instanceof Object);//true
System.out.println(object instanceof Teacher);//false
System.out.println(object instanceof String);//true
System.out.println("==================================================");
Person person = new Student();
System.out.println(person instanceof Student);//true
System.out.println(person instanceof Person);//true
System.out.println(person instanceof Object);//true
System.out.println(person instanceof Teacher);//false
// System.out.println(person instanceof String);//false
System.out.println("==================================================");
Student student = new Student();
System.out.println(student instanceof Student);
System.out.println(student instanceof Person);
System.out.println(student instanceof Object);
// System.out.println(student instanceof Teacher);
// System.out.println(student instanceof String);
}
}
強制型別轉換例項:
向下轉:強制轉換,可以使用子類方法,和父類方法。
package com.oop.Demo06;
public class Application03 {
public static void main(String[] args) {
//型別之間的轉換:高轉低
//高 低
Person obj = new Student();
//student將這個物件轉換為Student型別,我們就可以使用Student型別的方法了!
((Student)obj).go();//子類方法
((Student)obj).run()//父類方法
}
}
向上轉:會丟失一些子類自己的一些方法。
package com.oop.Demo06;
public class Application03 {
public static void main(String[] args) {
//型別之間的轉換:高轉低
Student student = new Student();
student.go();
Person person = student;
//子類轉換為父類,可能丟失自己本來的一些方法!
// person.go();
}
}
Java面向物件14:static關鍵字詳解
tip:static修飾的變數和方法是隨類一起載入的。呼叫非靜態方法只能new一個物件,通過物件呼叫該方法。非靜態方法可以訪問類中的靜態方法。靜態方法也可以呼叫類中靜態方法,但不能呼叫非靜態方法。
new物件時,關於static的一些執行順序:
package com.oop.Demo07;
public class Person {
//2 賦初始值
{
System.out.println("匿名程式碼塊");
}
//1 只執行一次
static {
System.out.println("靜態程式碼塊");
}
//3
public Person(){
System.out.println("構造方法");
}
public static void main(String[] args) {
Person person = new Person();
System.out.println("==================================");
Person person1 = new Person();
}
}
關於靜態匯入包:
package com.oop.Demo07;
//靜態匯入包:就可以直接使用Math中的方法,不需要加Math字首
import static java.lang.Math.random;
import static java.lang.Math.PI;
public class Test {
public static void main(String[] args) {
System.out.println(random());
System.out.println(PI);
}
}
tip:另外,被final修飾的類無法被繼承!
Java面向物件15:抽象類
abstract修飾符可以用來修飾方法也可以修飾類,如果修飾方法,那麼該方法就是抽象方法;如果修飾類,那麼該類就是抽象類。
package com.oop.Demo08;
//抽象類
public abstract class Action {
//約束,有人幫我們實現
//abstract,抽象方法,只有方法名字,沒有方法的實現!
public abstract void doSomething();
}
package com.oop.Demo08;
public class ActionSon extends Action {
@Override
public void doSomething() {
}
}
tip:
- 抽象類中可以沒有抽象的方法,但是有抽象方法的類一定要宣告為抽象類。
- 抽象類,不能使用new關鍵字來建立物件,它是用來讓子類繼承的。
- 抽象方法,只有方法的宣告,沒有方法的實現,它是用來讓子類實現的,
- 子類繼承抽象類,那麼就必須要實現抽象類沒有實現的抽象方法,否則該子類也要宣告為抽象類。
- Java的類為單繼承,介面可以多繼承。
特點:
- 抽象類不能new出來,只能靠子類去實現它:他就是一個約束!
- 抽象類中可以寫普通方法。
- 一旦有抽象方法,必須宣告為抽象類。抽象方法必須在抽象類中!
- 抽象的抽象。
思考題:不能new,那存在構造器嗎?存在的意義是什麼?(提高開發效率
答:
Java面向物件16:介面的定義與實現
普通類:只有具體實現。
抽象類:具體實現和規範(抽象方法)都有!
介面:只有規範!(自己無法寫方法,(專業的約束!)約束和實現分離
tip:介面不能被例項化,介面中沒有構造方法。
作用:
- 約束
- 定一些方法,讓不同的人實現。
- public abstract
- public static final
- 介面不能被例項化,介面中沒有構造方法。
- implements可以實現多個介面。
- 必須要重寫介面中的方法。
package com.oop.Demo09;
//interface 定義的關鍵字,將class替換掉了
public interface UserService {
//介面中的所有定義的變數 都是全域性靜態常量
/*public static final */int AGE = 99;
//介面中的所有定義的方法其實都是抽象的 public abstract
/*public abstract */void add(String name);
void delete(String name);
void update(String name);
void query(String name);
}
package com.oop.Demo09;
public interface TimeService {
void timer();
}
package com.oop.Demo09;
//抽象類:通過extends
//類可以實現介面 implements 介面
//實現了介面的類就需要重寫介面中的方法!
public class UserServiceImp implements UserService,TimeService{
@Override
public void add(String name) {
}
@Override
public void delete(String name) {
}
@Override
public void update(String name) {
}
@Override
public void query(String name) {
}
//多繼承,利用介面實現多繼承
@Override
public void timer() {
}
}
Java面向物件17:N種內部類
內部類就是在一個類的內部在定義一個類,比如,A類中定義一個B類,那麼B類相對A類來說就稱為內部類,而A類相對B類來說就是外部類。
-
成員內部類
-
靜態內部類
-
區域性內部類
-
匿名內部類
-
成員內部類
package com.oop.Demo10;
public class Outer {
private int id;
public void out(){
System.out.println("這是外部類的方法");
}
class Inner{
public void in(){
System.out.println("這是內部類的方法");
}
//獲得外部類的私有屬性
public void getId(){
System.out.println(id);
}
}
}
package com.oop.Demo10;
public class Application {
public static void main(String[] args) {
Outer outer = new Outer();
//通過這個外部類來例項化內部類
Outer.Inner inner = outer.new Inner();
inner.in();
inner.getId();
}
}
如果Inner加上public static,那麼就成為靜態內部類,靜態內部類無法訪問非靜態屬性id。
- tip:一個.java檔案裡面可以有多個類,但是隻能有一個public的類,測試類就可以直接寫在下面。
- 物件的區域性內部類:
package com.oop.Demo10;
public class Outer02 {
//區域性內部類
public void method(){
class Inner{
public void in(){
}
}
}
}
- 匿名內部類
package com.oop.Demo10;
public class Test {
public static void main(String[] args) {
//沒有名字就初始化類,匿名物件的使用,不用將例項儲存到變數中
new Apple().eat();
}
}
class Apple{
public void eat(){
System.out.println("eat");
}
}
- 介面的匿名內部類