Java面向對象
Java面向對象
面向過程&面向對象
面向過程:
- 步驟清晰簡單,第一步做什麼,第二步做什麼……
- 面向過程適合處理一些較為簡單的事情
面向對象:
- 物以類聚,分類的思維模式。思考問題首先會解決問題需要哪些分類,然後對這些分類進行單獨思考。最後,才對某個分類下的細節進行面向過程的思索。
- 面向對象適合處理複雜的問題,適合處理需要多人協作的問題。
概述:對於描述複雜的事物,為了從宏觀上把握、從整體上合理分析,我們需要使用面向對象的思路來分析整個系統。但是,具體到微觀操作,仍然需要面向過程的思路去處理。
什麼是面向對象?
面向對象(Object-Oriented Programming, OOP)的本質是以類的方式組織代碼,以對象的方式封裝數據。
三大特性:
- 封裝
- 繼承
- 多態
注意:
- 從認識論的角度考慮,是先有對象後有類。因為對象是具體的,而類是抽象的。類是對對象的抽象。
- 從代碼運行的角度考慮,是先有類後有對象。類是對象的模板。
類與對象的關係
類是一種抽象的數據類型。它是對某一類事物的整體描述或定義,但並不能代表某一個具體的事物。
- 如:人、動物、植物、電腦、手機,等等……
對象是抽象概念的具體實例。
- 如:張三、隔壁家的小花貓咪咪、《唐伯虎點秋香》里的那隻名叫旺財的狗
初始化與創建對象
創建的方式:使用new關鍵字創建對象
使用new關鍵字創建對象的時候,除了分配內存空間之外,還會給創建好的對象賦默認值進行初始化,以及調用類中的構造器。
示例:
Student類
package com.wmwx.oop.Demo01;
//學生類
public class Student {
//屬性:字段
String name;
int age;
//方法
public void study(){
System.out.println("學生"+this.name+"在學習。");
}
}
Application類(啟動類)
package com.wmwx.oop.Demo01;
//啟動類
public class Application {
//一個項目應該只存在一個main方法
public static void main(String[] args) {
//類是抽象的,需要實例化
//類實例化後會返回一個自己的對象
//student對象是Student類的一個實例
Student student = new Student();
student.study(); //輸出"學生null在學習。"
student.name = "小明";
student.study(); //輸出"學生小明在學習。"
}
}
構造方法
當一個對象被創建時候,構造方法用來初始化該對象。構造方法和它所在類的名字相同,但構造方法沒有返回值。
一個類即使什麼都不寫,也會存在一個構造方法。因為 Java 自動提供了一個默認構造方法,其訪問修飾符和類的訪問修飾符相同。
一旦自己定義了構造方法,默認的構造方法就會失效。
示例:
Person類:
package com.wmwx.oop.Demo01;
public class Person {
String name;
//使用快捷鍵alt+insert可以自動生成構造方法
//無參構造
public Person(){
this.name = "一個無名氏";
}
//有參構造(一旦定義有參構造,就必須顯式定義無參構造)
public Person(String name){
this.name = name;
}
}
Application類:
package com.wmwx.oop.Demo01;
public class Application {
public static void main(String[] args) {
//使用new關鍵字,本質是在調用構造方法
Person person1 = new Person(); //調用無參構造
System.out.println(person1.name); //輸出"一個無名氏"
//利用構造方法,可以初始化對象
Person person2 = new Person("惟妙惟霄"); //調用有參構造
System.out.println(person2.name); //輸出"惟妙惟霄"
}
}
內存分析
過程如下:
- 創建類
- 在堆中存放類和類中的靜態方法
- 創建對象
- 在堆中為對象開闢空間
- 在棧中存放對象的引用變量名
- 令對象的引用變量名指向堆中開闢的空間
封裝
所謂封裝,即該露的露,該藏的藏。程序設計要追求「高內聚,低耦合」。
高內聚:類的內部數據操作細節自己完成,不允許外部干涉。
低耦合:僅暴露少量的方法給外部使用。
對於代碼而言,總結起來就一句話:屬性私有,get/set。
意義:
- 提升程序的安全性,保護數據
- 隱藏代碼的實現細節
- 統一接口
- 提高了系統的可維護性
示例:
Student類:
package com.wmwx.oop.Demo03;
public class Student {
//屬性私有
private String name;
private int id;
private String gender;
private int age;
//需要提供共有的get和set方法
//get方法:獲取數據
public String getName() {
return name;
}
//set方法:設置數據
public void setName(String name) {
this.name = name;
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getGender() {
return gender;
}
public void setGender(String gender) {
this.gender = gender;
}
public int getAge() {
return age;
}
public void setAge(int age) {
if (age<0||age>120){
this.age = 3;
}else{
this.age = age;
}
}
}
Application類:
package com.wmwx.oop;
import com.wmwx.oop.Demo03.Student;
//啟動類
public class Application {
public static void main(String[] args) {
Student student = new Student();
student.setName("惟妙惟霄");
student.setId(123456);
student.setGender("男");
student.setAge(130);
System.out.println(student.getAge()); //輸出3
}
}
繼承
繼承的本質是對某一批類的抽象,從而實現對現實世界更好的建模。
在 Java 中通過 extends 關鍵字可以聲明一個類是從另外一個類繼承而來的,其語法如下:
class 子類 extends 父類 {
}
extends的意思是擴展,子類是對父類的擴展。
繼承的注意事項:
-
Java只有單繼承,沒有多繼承。
-
在Java中,所有類都默認直接或間接繼承自Object類。
在Java中,可以使用this指代當前類,並使用super指代父類。
super的注意事項:
- super調用父類的構造方法,必須在構造方法的第一行。
- super只能出現在子類的方法或構造方法中。
- super和this不能同時調用構造方法。
重寫是子類對父類的允許訪問的方法的實現過程進行重新編寫, 返回值和形參都不能改變。重寫的好處在於子類可以根據需要,定義特定於自己的行為。 也就是說,子類能夠根據需要實現父類的方法。重寫需要遵守以下規則:
- 方法名必須相同
- 參數列表必須相同
- 修飾符的範圍可以擴大,但不能縮小
- 拋出的異常可以被縮小,但不能擴大
示例:
Person類:
package com.wmwx.oop.Demo04;
//父類:人類
public class Person {
private int money = 10_0000_0000;
protected String name = "惟妙惟霄";
public Person() {
System.out.println("Person的無參構造方法執行了!");
}
public void say(){
System.out.println("說了一句話。");
}
public int getMoney() {
return money;
}
public void setMoney(int money) {
this.money = money;
}
public void print(){
System.out.println("Person");
}
}
Student類:
package com.wmwx.oop.Demo04;
//子類:學生類
public class Student extends Person{
private String name = "妙霄";
public Student() {
//隱藏代碼:super();
//父類的構造方法必須要在子類的構造方法的第一行
System.out.println("Student的無參構造方法執行了!");
}
public void test1(String name){
System.out.println(name); //輸出參數
System.out.println(this.name); //輸出當前類的name
System.out.println(super.name); //輸出父類的name
}
//重寫都是方法的重寫,與屬性無關
//只允許重寫public方法
//可以使用快捷鍵alt+insert來插入重寫方法
@Override
public void print() {
System.out.println("Student");
}
public void test2(){
print();
this.print();
super.print();
}
}
Application類:
package com.wmwx.oop;
import com.wmwx.oop.Demo04.Person;
import com.wmwx.oop.Demo04.Student;
//啟動類
public class Application {
public static void main(String[] args) {
Student student = new Student();
//第一行輸出"Person的無參構造方法執行了!"
//第二行輸出"Student的無參構造方法執行了!"
student.say(); //子類繼承父類,就會擁有父類的public方法
System.out.println(student.getMoney()); //可以用父類的get/set方法對屬性進行操作
//可以使用快捷鍵ctrl+H來查看繼承樹
student.test1("MiaoXiao");
//第一行輸出"MiaoXiao"
//第二行輸出"妙霄"
//第三行輸出"惟妙惟霄"
student.test2();
//第一行輸出"Student"
//第二行輸出"Student"
//第三行輸出"Person"
Student stu1 = new Student();
stu1.print(); //輸出"Student"
//父類的引用指向了子類
Person stu2 = new Student();
stu2.print(); //輸出"Student"
}
}
多態
多態是同一個行為具有多個不同表現形式或形態的能力。當使用多態方式調用方法時,首先檢查父類中是否有該方法,如果沒有,則編譯錯誤;如果有,再去調用子類的同名方法。
多態存在的三個必要條件:
- 繼承
- 重寫
- 父類引用指向子類對象:
Parent p = new Child();
不能被重寫的方法:
- static 方法
- final 方法
- private 方法
示例:
Person類:
package com.wmwx.oop.Demo05;
public class Person {
public void run(){
System.out.println("人在跑步。");
}
}
Student類:
package com.wmwx.oop.Demo05;
public class Student extends Person{
@Override
public void run() {
System.out.println("學生在跑步。");
}
public void eat(){
System.out.println("學生在吃東西。");
}
}
Application類:
package com.wmwx.oop;
import com.wmwx.oop.Demo05.Person;
import com.wmwx.oop.Demo05.Student;
//啟動類
public class Application {
public static void main(String[] args) {
//一個對象的實際類型是確定的
//但可以指向的引用類型是不確定的
Student s1 = new Student();
Person s2 = new Student(); //父類的引用指向子類
Object s3 = new Student();
s1.run(); //輸出"學生在跑步"
s2.run(); //子類重寫父類方法,將執行子類方法,輸出"學生在跑步。"
s1.eat(); //輸出"學生在吃東西"
//s2.eat(); //不能調用。對象能使用哪些方法,要看左邊的類型。
((Student)s2).eat(); //強制類型轉換。輸出"學生在吃東西。"
}
}
instanceof
Java中的instanceof關鍵字可以用來判斷某一個對象是不是某一個類是實例。如果是,返回true;如果不是,返回false;如果二者無關,則編譯不通過。
示例:
package com.wmwx.oop;
import com.wmwx.oop.Demo06.Person;
import com.wmwx.oop.Demo06.Student;
import com.wmwx.oop.Demo06.Teacher;
//啟動類
public class Application {
public static void main(String[] args) {
//繼承關係如下:
//Object -> Person -> Student
//Object -> Person -> Teacher
//Object -> String
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); //輸出false
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); //編譯時報錯
System.out.println("=====");
Student student = new Student();
System.out.println(student instanceof Student); //輸出true
System.out.println(student instanceof Person); //輸出true
System.out.println(student instanceof Object); //輸出true
//System.out.println(student instanceof Teacher);//編譯時報錯
//System.out.println(student instanceof String); //編譯時報錯
}
}
類型轉換
- 子類轉換為父類,是向上轉型,可自動轉換。
- 父類轉換為子類,是向下轉型,需強制轉換。
示例:
Person類:
package com.wmwx.oop.Demo06;
public class Person {
public void run(){
System.out.println("人在跑步。");
}
}
Student類:
package com.wmwx.oop.Demo06;
public class Student extends Person{
public void walk(){
System.out.println("學生在走路。");
}
}
Application類:
package com.wmwx.oop;
import com.wmwx.oop.Demo06.Person;
import com.wmwx.oop.Demo06.Student;
//啟動類
public class Application {
public static void main(String[] args) {
//高 ----------------- 低
Person person = new Student();
((Student)person).walk(); //強制類型轉換
Student student = new Student();
Person obj = student; //子類轉換為父類,可能會丟失一些方法
//obj.walk(); //編譯時報錯
}
}
靜態代碼塊
靜態代碼塊會在類加載時執行,且只會執行一次。
示例:
Person類:
package com.wmwx.oop.Demo07;
public class Person {
//第二個執行,可在這裡賦初始值
{
System.out.println("匿名代碼塊");
}
//第一個執行,只執行一次
static {
System.out.println("靜態代碼塊");
}
//第三個執行
public Person() {
System.out.println("構造方法");
}
}
Application類:
package com.wmwx.oop;
import com.wmwx.oop.Demo07.Person;
//啟動類
public class Application {
public static void main(String[] args) {
Person person = new Person();
//第一行輸出"靜態代碼塊"
//第二行輸出"匿名代碼塊"
//第三行輸出"構造方法"
}
}
抽象類
在Java語言中使用abstract 來定義抽象類,其基本語法如下:
abstract class 類名{
//屬性
//方法
}
抽象類除了不能實例化對象之外,類的其它功能依然存在,成員變量、成員方法和構造方法的訪問方式和普通類一樣。
由於抽象類不能實例化對象,所以抽象類必須被繼承,才能被使用。在Java中一個類只能繼承一個抽象類,但一個類可以實現多個接口。
在Java語言中使用abstract 來定義抽象方法,其基本語法如下:
abstract 訪問修飾符 返回值類型 方法名(參數);
抽象類與抽象方法的規則:
- 抽象類不能被實例化(即不能被 new ),只有抽象類的非抽象子類可以創建對象。
- 抽象類中不一定包含抽象方法,但是有抽象方法的類必定是抽象類。
- 抽象類中的抽象方法只是聲明,不包含方法體,也就是不給出方法的具體實現。
- 構造方法、類方法(用 static 修飾的方法)不能聲明為抽象方法。
- 抽象類的子類必須給出抽象類中的抽象方法的具體實現,除非該子類也是抽象類。
示例:
Action類:
package com.wmwx.oop.Demo08;
//使用abstract聲明抽象類
public abstract class Action {
//抽象方法,只有方法名,沒有方法體
//僅作約束,期待他人實現
public abstract void doSomething();
}
A類:
package com.wmwx.oop.Demo08;
public class A extends Action{
//子類必須實現父類的抽象方法
//除非該子類是抽象類
@Override
public void doSomething() {
}
}
接口
接口在JAVA編程語言中是一個抽象類型,是抽象方法的集合,通常以 interface 來聲明。其基本語法如下:
[訪問修飾符] interface 接口名稱 [extends 其他的接口名稱] {
// 抽象方法
}
接口的特性:
- 接口中每一個方法都是隱式抽象的,接口中的方法會被隱式地指定為 public abstract,並且只能是 public abstract。
- 接口中可以含有變量,但是接口中的變量會被隱式地指定為 public static final ,並且只能是 public static final。
- 接口中的方法不能在接口中實現,只能由實現接口的類來實現接口中的方法。
- 一個接口能繼承另一個接口,使用 extends 關鍵字.子接口會繼承父接口的方法。
當類實現接口的時候,需要實現接口中所有的方法。否則,類必須聲明為抽象的類。Java中使用 implements 關鍵字實現接口,其基本語法如下:
class 類名 implements 方法名{
//實現接口中的抽象方法
}
示例:
UserService接口:
package com.wmwx.oop.Demo09;
//定義接口使用關鍵字interface
public interface UserService {
//接口中的所有屬性都是public static final
//一般不在接口中定義屬性
int age = 60;
//接口中的所有方法都是public abstract
void add(String name);
void delete(String name);
void update(String name);
void query(String name);
}
TimeService接口:
package com.wmwx.oop.Demo09;
public interface TimeService {
void timer();
}
UserServiceImpl類:
package com.wmwx.oop.Demo09;
//類使用關鍵字implements來實現接口
//實現了接口的類,需要重寫接口的所有方法
//一個類可以實現多個接口
public class UserServiceImpl 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() {
}
}
內部類
所謂內部類,就是在一個類的內部再定義一個類。
示例:
Outer類:
package com.wmwx.oop.Demo10;
//外部類
public class Outer {
private int id=10;
public void out(){
System.out.println("這是外部類的方法!");
}
public class Inner{
public void in(){
System.out.println("這是內部類的方法!");
}
//獲得外部類的私有屬性
public int getId(){
return id;
}
}
public void method(){
//局部內部類,在外部類的方法之中
class Inner{
}
}
}
//一個java文件可以有多個class,但是只能有一個public class
class A{
}
Application類:
package com.wmwx.oop;
import com.wmwx.oop.Demo09.UserService;
import com.wmwx.oop.Demo10.Outer;
//啟動類
public class Application {
public static void main(String[] args) {
//外部類使用new關鍵字
Outer outer = new Outer();
outer.out();
//內部類通過外部類來實例化
Outer.Inner inner = outer.new Inner();
inner.in();
System.out.println(inner.getId());
//匿名類,不用將實例保存到變量中
new Outer().out();
//使用匿名類實現接口
UserService userService = new UserService() {
@Override
public void add(String name) {
}
@Override
public void delete(String name) {
}
@Override
public void update(String name) {
}
@Override
public void query(String name) {
}
}
}
}