Java面向對象詳解-上

1、類及對象

1. 類的組成成分

  • 屬性(成員變量,Field)
  • 方法(成員方法,函數,Method)

2. 屬性

成員變量 vs 局部變量前端

  • 相同點:
    • 遵循變量聲明的格式: 數據類型 變量名 = 初始化值
    • 都有做用域
  • 不一樣點:
    • 聲明的位置的不一樣 :成員變量:聲明在類裏,方法外, 局部變量:聲明在方法內,方法的形參部分,代碼塊內
    • 成員變量的修飾符有四個:public private protected 缺省,局部變量沒有修飾符,與所在的方法修飾符相同
    • 初始化值:必定會有初始化值,成員變量:若是在聲明的時候,不顯式的賦值,那麼不一樣數據類型會有不一樣的默認初始化值。 局部變量:必定要顯式的賦值。(局部變量沒有默認初始化值
      • byte short int long ==>0
      • float double ==>0.0
      • char ==>空格
      • boolean ==>false
      • 引用類型變量==>null
    • 兩者在內存中存放的位置不一樣:成員變量存在於堆空間中;局部變量:棧空間中
  • 總結:
    • 按照數據類型的不一樣:基本數據類型(8種) & 引用數據類型
    • 按照聲明的位置的不一樣:成員變量 & 局部變量

3. 方法

提供某種功能的實現java

public void eat(){//方法體}
    public String getName(){}
    public void setName(String n){}
    //格式:權限修飾符 返回值類型(void:無返回值/具體的返回值) 方法名(形參){}
  • 關於返回值類型
    • void:代表此方法不須要返回值
    • 有返回值的方法:在方法的最後必定有return + 返回值類型對應的變量
  • 方法內能夠調用本類的其餘方法或屬性,可是不能在方法內再定義方法!

4. 面向對象編程的思想的落地法則一:

  • 設計並建立類及類的成分
  • 實例化類的對象
  • 經過「對象.屬性」或"對象.方法"的形式完成某項功能

5. 類的初始化內存解析:內存劃分的結構

  • 棧(stack):局部變量 、對象的引用名、數組的引用名
  • 堆(heap):new 出來的「東西」(如:對象的實體,數組的實體),含成員變量
  • 方法區:含字符串常量
  • 靜態域:聲明爲static的變量

6. 萬事萬物皆對象

  • 在Java語言範疇中,咱們都能將功能、結構等封裝到類中,經過類的實體化,來調用具體的功能結構
    • Scanner、String
    • 文件:File
  • 設計到Java語言與前端Html、後端的數據庫交互時,都體現爲類、對象

7. 例題

/*
 * 4. 對象數組題目:
定義類Student,包含三個屬性:學號number(int),年級state(int),成績score(int)。
 建立20個學生對象,學號爲1到20,年級和成績都由隨機數肯定。
問題一:打印出3年級(state值爲3)的學生信息。
問題二:使用冒泡排序按學生成績排序,並遍歷全部學生信息

提示:
1) 生成隨機數:Math.random(),返回值類型double;  
2) 四捨五入取整:Math.round(double d),返回值類型long。
 * 
 * 
 *  // 兩位數的,隨機數  10 - 99
  公式:【a,b】 :   Math.random()*(b-a+1)+a   再強轉數據類型。
 * 
 */
public class StudentTest {
	public static void main(String[] args) {

		// 聲明Student類型的數組
		Student[] stu = new Student[20];

		for (int i = 0; i < stu.length; i++) {
			// 給數組元素賦值
			stu[i] = new Student();
			stu[i].number = i + 1;
			// [1,6]
			stu[i].state = (int) (Math.random() * (6 - 1 + 1) + 1);
			// [0,100]
			stu[i].score = (int) (Math.random() * (100 - 0 + 1));
		}

		StudentTest test = new StudentTest();
		
		// 問題1
		test.searchState(stu,3);
		System.out.println("------------------------");
		//
		// 問題2
		test.sort(stu);
		test.print(stu);
		
	}

	
	// 遍歷學生數組
	public void print(Student[] stu) {
		for (int i = 0; i < stu.length; i++) {
			System.out.println(stu[i].info());
		}
	}

	/**
	 * 
	 * @Description  查找指定年紀的學生
	 * @author MD
	 * @date 2020年7月6日下午12:02:56
	 * @param stu 查找的數組
	 * @param state 指定的年紀
	 */
	public void searchState(Student[] stu, int state) {
		for (int i = 0; i < stu.length; i++) {
			if (stu[i].state == 3)
				System.out.println(stu[i].info());
		}
	}
	
	
	public void sort(Student[] stu) {
		for (int i = 0; i < stu.length - 1; i++) {
			for (int j = 0; j < stu.length - i - 1; j++) {
				if (stu[j].score <= stu[j + 1].score) {
					// 注意,這裏交換的不是成績而是對象
					Student temp = stu[j];
					stu[j] = stu[j + 1];
					stu[j + 1] = temp;
				}
			}
		}
	}

}

class Student {
	int number;
	int state;
	int score;

	public String info() {
		return "學號:" + number + " 年級:" + state + " 分數:" + score;
	}

}

2、方法的重載(overload)

要求:
* 同一個類中
* 方法名必須相同
* 方法的參數列表不一樣(①參數的個數不一樣②參數類型不一樣
補充:方法的重載與方法的返回值類型沒有關係!面試

//以下的四個方法構成重載
//定義兩個int型變量的和
public int getSum(int i,int j){
    return i + j;
}
//定義三個int型變量的和
public int getSum(int i,int j,int k){
    return i + j + k;
}
//定義兩個double型數據的和
public double getSum(double d1,double d2){
    return d1 + d2;
}

//定義三個double型數組的和
public void getSum(double d1,double d2,double d3){
    System.out.println(d1 + d2 + d3);
}
//不能與如上的幾個方法構成重載
//  public int getSum1(int i,int j,int k){
//      return i + j + k;
//  }
//  public void getSum(int i,int j,int k){
//      System.out.println(i + j + k);
//  }


//如下的兩個方法構成重載。
public void method1(int i,String str){
    
}
public void method1(String str1,int j){
    
}

3、可變個數的形參的方法

  • .格式:對於方法的形參: 數據類型 ... 形參名
  • 可變個數的形參的方法與同名的方法之間構成重載
  • 可變個數的形參在調用時,個數從0開始,到無窮多個均可以
  • 使用可變多個形參的方法與方法的形參使用數組是一致
  • 若方法中存在可變個數的形參,那麼必定要聲明在方法形參的最後
  • 在一個方法中,最多聲明一個可變個數的形參
//以下四個方法構成重載
    //在類中一旦定義了重載的可變個數的形參的方法之後,以下的兩個方法能夠省略
//  public void sayHello(){
//      System.out.println("hello world!");
//  }
//  public void sayHello(String str1){
//      System.out.println("hello " + str1);
//  }
    //可變個數的形參的方法
    public void sayHello(String ... args){
        for(int i = 0;i < args.length;i++){
            System.out.println(args[i] + "$");
        }
        //System.out.println("=====");
    }
    
    public void sayHello(int i,String ... args){
    //public void sayHello(String ... args,int i){
        System.out.println(i);
        
        for(int j = 0;j < args.length;j++){
            System.out.println(args[j] + "$");
        }
    }
    
    public void sayHello1(String[] args){
        for(int i = 0;i < args.length;i++){
            System.out.println(args[i]);
        }
    }

4、 Java的值傳遞

  • 方法的參數傳遞(重點、難點)
    • 形參:方法聲明時,方法小括號內的參數
    • 實參:調用方法時,實際傳入的參數的值
  • java中的參數傳遞機制:值傳遞機制
    • 形參是基本數據類型的:將實參的值傳遞給形參的基本數據類型的變量
    • 形參是引用數據類型的:將實參的引用類型變量的值(對應的堆空間的對象實體的首地址值)傳遞給形參的引用類型變量
  • 關於變量的賦值
    • 若是變量是基本數據類型,此時賦值的是變量所保存的數據值
    • 若是變量是引用數據類型,此時賦值的變量是所保存的地址值

1. 例一

public static void main(String[] args) {
    TestArgsTransfer tt = new TestArgsTransfer();
    
    int i = 10;
    int j = 5;
    System.out.println("i:" + i + " j:" + j);//i : 10  j : 5
    
//      //交換變量i與j的值
//      int temp = i;
//      i = j;
//      j = temp;
    tt.swap(i, j);//將i的值傳遞給m,j的值傳遞給n
    
    
    System.out.println("i:" + i + " j:" + j);//i : 10  j : 5
    
}
//定義一個方法,交換兩個變量的值
public void swap(int m,int n){
    int temp = m;
    m = n;
    n = temp;
    System.out.println("m:" + m + " n:" + n);

}

2. 例二

public class TestArgsTransfer1 {
    public static void main(String[] args) {
        TestArgsTransfer1 tt = new TestArgsTransfer1();
        DataSwap ds = new DataSwap();
        
        System.out.println("ds.i:" + ds.i + " ds.j:" + ds.j);
        
        tt.swap(ds);
        System.out.println(ds);
        
        System.out.println("ds.i:" + ds.i + " ds.j:" + ds.j);
        
    }
    //交換元素的值
    public void swap(DataSwap d){
        int temp = d.i;
        d.i = d.j;
        d.j = temp;
        System.out.println(d);//打印引用變量d的值
    }
}

class DataSwap{
    int i = 10;
    int j = 5;
}

3. 例3

package com.atguigu.exer;

import java.io.PrintStream;

public class Test {

	public static void main(String[] args) {
		int a = 10;
		int b = 10;
		method(a,b);
		System.out.println("a="+a);
		System.out.println("b="+b);
	}
	
//	public static void method(int a , int b) {
//		a = a * 10;
//		b = b * 20;
//		System.out.println(a);
//		System.out.println(b);
//		System.exit(0);
//	}
	public static void method(int a, int b) {
		PrintStream ps = new PrintStream(System.out) {
			public void println(String x){
				if("a=10".equals(x)) {
					x = "a=100";
				}else if("b=10".equals(x)) {
					x = "b=200";
				}
				super.println(x);
			}
		};
		
		System.setOut(ps);
	}

	
}

4. 例4

輸出的什麼?sql

public class Test1 {

	public static void main(String[] args) {
		int[] arr = new int[] {1,2,3};
		System.out.println(arr); //地址值
		
		char[] arr1 = new char[] {'a','b','c'};
		System.out.println(arr1); //abc
	}
}

5、面向對象的特徵一:封裝

  • 問題:當建立了類的對象之後,若是直接經過"對象.屬性"的方式對相應的對象屬性賦值的話,可能會出現不知足實際狀況的意外,咱們考慮不讓對象來直接做用屬性,而是經過"對象.方法"的形式,來控制對象對屬性的訪問。實際狀況中,對屬性的要求就能夠經過方法來體現
  • 高內聚,低耦合
  • 面向對象思想的落地法則二:
    • 將類的屬性私有化
    • 提供公共的方法(setter & getter)來實現調用
  • 四種權限修飾符
    • 權限從大到小爲:public protected 缺省 private
    • 四種權限均可以用來修飾屬性、方法、構造器
    • 修飾類的話:public 缺省

  • 封裝性的體現
    • 將類的屬性私有化,提供公共的方法來調用
    • 不對外暴露的私有化方法
    • 單例模式

1. 構造器

構造器的做用:①建立對象 ②給建立的對象的屬性賦值數據庫

  • 設計類時,若不顯式聲明類的構造器的話,程序會默認提供一個空參的構造器
  • 一旦顯式的定義類的構造器,那麼默認的構造器就再也不提供
  • 如何聲明類的構造器。格式:權限修飾符 類名(形參){ }
  • 類的多個構造器之間構成重載
  • 類對象的屬性賦值的前後順序:
    • 屬性的默認初始化
    • 屬性的顯式初始化
    • 經過構造器給屬性初始化
    • 經過"對象.方法"的方式給屬性賦值
  • 一個類中至少會有一個構造器,通常都提供一個空參的構造器

2. this關鍵字

  • 使用在類中,能夠用來修飾屬性、方法、構造器
  • 表示當前對象或者是當前正在建立的對象
  • 當形參與成員變量重名時,若是在方法內部須要使用成員變量,必須添加this來代表該變量時類成員
  • 在任意方法內,若是使用當前類的成員變量或成員方法能夠在其前面添加this,加強程序的閱讀性
  • 在構造器中使用「this(形參列表)」顯式的調用本類中重載的其它的構造器,要求「this(形參列表)」要聲明在構造器的首行!
public class TestPerson {
    public static void main(String[] args) {
        Person p1 = new Person();
        System.out.println(p1.getName() + ":" + p1.getAge());
        
        Person p2 = new Person("BB",23);
        int temp = p2.compare(p1);
        System.out.println(temp);
    }
}
class Person{
    
    private String name;
    private int age;
    
    public Person(){
        this.name = "AA";
        this.age = 1;
    }
    
    public Person(String name){
        this(); // 先調用空參數的
        this.name = name;
    }
    public Person(String name,int age){
        this(name);
        this.age = age;
    }
    
    public String getName() {
        return name;
    }
    public void setName(String name) {
        this.name = name;
    }
    public int getAge() {
        return age;
    }
    public void setAge(int age) {
        this.age = age;
    }
    public void eat(){
        System.out.println("eating");
    }
    public void sleep(){
        System.out.println("sleeping");
        this.eat();
    }
    //比較當前對象與形參的對象的age誰大。
    public int compare(Person p){
        if(this.age > p.age)
            return 1;
        else if(this.age < p.age)
            return -1;
        else
            return 0;
    }
    
}

3. package/import

package: 聲明源文件所在的包,寫在程序的第一行
import:編程

  • 顯式導入指定包下的類或接口
  • 寫在包的聲明和源文件之間
  • 若是須要引入多個類或接口,那麼就並列寫出
  • 若是導入的類是java.lang包下的,如:System String Math等,就不須要顯式的聲明
  • 理解.*的概念。好比java.util. *;
  • 導入java.lang.*只能導入lang包下的全部類或接口,不能導入lang的子包下的類或接口
  • import static 表示導入指定類的static的屬性或方法
/import java.util.Scanner;
//import java.util.Date;
//import java.util.List;
//import java.util.ArrayList;
import java.lang.reflect.Field;
import java.util.*;
import static java.lang.System.*;
public class TestPackageImport {
    public static void main(String[] args) {
        out.println("helloworld");
        Scanner s = new Scanner(System.in);
        s.next();
        
        Date d = new Date();
        List list = new ArrayList();
        
        java.sql.Date d1 = new java.sql.Date(522535114234L);
        
        Field f = null;
    }
}

6、面向對象的特徵二:繼承

  1. 繼承的格式
    • 經過"class A extends B"類實現類的繼承
  2. 子類繼承父類之後,父類中聲明的屬性、方法,子類就能夠獲取到
    • 當父類中有私有的屬性或方法時,子類一樣能夠獲取獲得,只是因爲封裝性的設計,使得子類不能夠直接調用罷了
    • 子類除了經過繼承,獲取父類的結構以外,還能夠定義本身的特有的成分
  3. java中類的繼承性只支持單繼承:一個類只能繼承一個父類。反之,一個父類能夠有多個子類
  4. 若是沒有顯示聲明一個類的父類的話,則此類繼承於java.lang.Object類

1. 方法的重寫(override orverwrite) vs 重載(overload)

  1. 重載:「兩同一不一樣」:同一個類,同一個方法名,不一樣的參數列表注:方法的重載與方法的返回值無關!構造器是能夠重載的
  2. 重寫:(前提:在繼承的基礎之上,子類在獲取了父類的結構之後,能夠對父類中同名的方法進行「重構」)方法的返回值,方法名,形參列表形同;權限修飾符不小於父類的同名方法;子類方法的異常類型不大於父類的;兩個方法要同爲static或同爲非static
    • 注:不能重寫父類的私有的方法
    • 父類被重寫的返回值類型是A類型,那麼子類重寫方法的返回值類型能夠是A類或者A類的子類
class Cirlce{
   //求圓的面積
    public double findArea(){
   
    } 

}

class Cylinder extends Circle{
      //求圓柱的表面積
      public double findArea(){
      }
}

2. 關鍵字 super

  1. super,相較於關鍵字this,能夠修飾屬性、方法、構造器
  2. super修飾屬性、方法:在子類的方法、構造器中,經過super.屬性或者super.方法的形式,顯式的調用父類的指定屬性或方法。尤爲是,當子類與父類有同名的屬性、或方法時,調用父類中的結構的話,必定要用「super.」
  3. 經過「super(形參列表)」,顯式的在子類的構造器中,調用父類指定的構造器!
  4. 任何一個類(除Object類)的構造器的首行,要麼顯式的調用本類中重載的其它的構造器「this(形參列表)」或顯式的調用父類中指定的構造器「super(形參列表)」,要麼默認的調用父類空參的構造器"super()",只能二選一
  5. 建議在設計類時,提供一個空參的構造器

3. 子類對象實例化的全過程

不管經過那個構造器建立子類對象,須要保證先初始化父類後端

目的:當子類繼承父類後,繼承父類中全部的屬性和方法,所以子類必須知道父類如何爲對象進行初始化數組

public class TestDog {
    public static void main(String[] args) {
        Dog d = new Dog();
        d.setAge(10);
        d.setName("小明");
        d.setHostName("花花");

        System.out.println("name:" + d.getName() + " age:" + d.getAge()
                + "hostName:" + d.getHostName());
        
        System.out.println(d.toString());
    }
}

// 生物
class Creator {
    private int age;

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public Creator() {
        super();
        System.out.println("this is Creator's constructor");
    }

}

// 動物類
class Animal extends Creator {
    private String name;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Animal() {
        super();
        System.out.println("this is Animal's constructor");
    }

}

// 狗
class Dog extends Animal {
    private String hostName;

    public String getHostName() {
        return hostName;
    }

    public void setHostName(String hostName) {
        this.hostName = hostName;
    }

    public Dog() {
        super();
        System.out.println("this is Dog's constructor");
    }
}

7、 面向對象的特徵三:多態

1. 多態性的表現:

①方法的重載與重寫 ②子類對象的多態性dom

2. 使用的前提:

①要有繼承關係 ②要有方法的重寫ide

3. 格式

  • Person p = new Man();//向上轉型
  • 經過父類的引用指向子類的對象實體,當調用方法時實際執行的是子類重寫父類的方法

編譯時,認爲p是Person類型的,故只能執行Person裏纔有的結構,即Man裏特有的結構不可以調用,子類對象的多態性,並不使用於屬性。

調用方法:編譯看左邊,運行看右邊

屬性:編譯和運行都看左邊

package com.atguigu.java;

public class AnimalTest {

	public static void main(String[] args) {
		
		AnimalTest test = new AnimalTest();
		// 多態性的體現
		test.func(new Dag());
		test.func(new Cat());
	}

	public void func(Animal an) {  // Animal an = new Dag();
		an.eat();
		an.shot();
	}
	
	
//	public void func(Dag dag) {
//		dag.eat();
//		dag.shot();
//	}
	
}

class Animal{
	public void eat() {
		System.out.println("動物:吃食物");
	}
	
	public void shot() {
		System.out.println("動物:叫");
	}
}

class Dag extends Animal{
	public void eat() {
		System.out.println("狗吃肉");
	}
	
	public void shot() {
		System.out.println("汪!汪");
	}
}

class Cat extends Animal{
	public void eat() {
		System.out.println("貓吃魚");
	}
	
	public void shot() {
		System.out.println("喵!喵");
	}
}

4. 關於向下轉型

有了對象的多態性以後,內存中其實是加載了子類特有的屬性和方法,可是因爲變量聲明爲父類類型,致使了編譯時只能調用父類中聲明的屬性和方法,子類中特有的屬性和方法調用不了,因此有了向下轉型

  • 向下轉型,使用強轉符:()
  • 爲了保證不報ClassCastException,最好在向下轉型前,進行判斷: instanceof
if (p1 instanceof Woman) {
            System.out.println("hello!");
            Woman w1 = (Woman) p1;
            w1.shopping();
  }

   if (p1 instanceof Man) {
        Man m1 = (Man) p1;
         m1.entertainment();
  }

5. 多態是編譯性行爲仍是運行時行爲

運行時行爲

package com.atguigu.java5;


import java.util.Random;

//面試題:多態是編譯時行爲仍是運行時行爲?
//證實以下:
class Animal  {
 
	protected void eat() {
		System.out.println("animal eat food");
	}
}

class Cat  extends Animal  {
 
	protected void eat() {
		System.out.println("cat eat fish");
	}
}

class Dog  extends Animal  {
 
	public void eat() {
		System.out.println("Dog eat bone");
	}
}

class Sheep  extends Animal  {

	public void eat() {
		System.out.println("Sheep eat grass");
	}
}

public class InterviewTest {

	public static Animal  getInstance(int key) {
		switch (key) {
		case 0:
			return new Cat ();
		case 1:
			return new Dog ();
		default:
			return new Sheep ();
		}
	}

	public static void main(String[] args) {
		int key = new Random().nextInt(3);

		System.out.println(key);

		Animal  animal = getInstance(key);
		
		animal.eat();
	}
}

6. 問題1

package com.atguigu.exer;
/*
 * 練習:
 * 1.若子類重寫了父類方法,就意味着子類裏定義的方法完全覆蓋了父類裏的同名方法,
 * 系統將不可能把父類裏的方法轉移到子類中:編譯看左邊,運行看右邊
 * 
 * 2.對於實例變量則不存在這樣的現象,即便子類裏定義了與父類徹底相同的實例變量,
 * 這個實例變量依然不可能覆蓋父類中定義的實例變量:編譯運行都看左邊
 */
class Base {
	int count = 10;

	public void display() {
		System.out.println(this.count);
	}
}

class Sub extends Base {
	int count = 20;

	public void display() {
		System.out.println(this.count);
	}
}

public class FieldMethodTest {
	public static void main(String[] args) {
		Sub s = new Sub();
		System.out.println(s.count);//20
		s.display();//20
		
		Base b = s;//多態性
		//==:對於引用數據類型來說,比較的是兩個引用數據類型變量的地址值是否相同
		System.out.println(b == s);//true
		System.out.println(b.count);//10
		b.display();//20
	}
}

7. 問題2

package com.atguigu.exer;

//考查多態的筆試題目:
public class InterviewTest1 {

	public static void main(String[] args) {
		Base1 base = new Sub1();
		base.add(1, 2, 3); //sub_1

		Sub1 s = (Sub1)base;
		s.add(1,2,3); //sub_2
	}
}

class Base1 {
	public void add(int a, int... arr) {
		System.out.println("base1");
	}
}

class Sub1 extends Base1 {

	public void add(int a, int[] arr) {
		System.out.println("sub_1");
	}

	public void add(int a, int b, int c) {
		System.out.println("sub_2");
	}

}
相關文章
相關標籤/搜索