1,類和對象的關係
對象:是具體的事物,我們叫做object,instance實例,以後我們說某個類的對象,某個類的實例,是一樣的意思
類:是對對象的抽象,我們叫做class,是對現實生活中事物的描述。
類和對象的關係:特殊到一般,具體到抽象。
類可以看成一類對象的模板,對象可以看成該類的一個具體實例。
類是用於描述同一類型的對象的一個抽象的概念,類中定義了這一類對象所應具有的靜態和動態屬性。
對象時JAVA程序的核心,在JAVA程序中“萬物皆對象”。
JDK中提供了很多類供編程人員使用,編程人員也可定義自己的類。
定義類 :
class 類名(首字母大寫){
//屬性
private 數據類型 屬性名; //建議增加相應的getter、setter方法
//構造方法
//方法 行爲
}
調用類例 :
Car c=new Car();
c.color;
在堆內存中產生一個實體 ,通過new操作符來完成。
2,成員變量和局部變量
1、局部變量
定義在方法內或者語句塊內 從屬於方法或者語句塊
使用之前,必須手動初始化。
2、成員變量
定義在類裏面、方法外面。從屬於對象
如果沒有手動初始化成員變量,系統會自動初始化。初始化的規則如下:
數字:0,0.0 布爾 false char \u0000 引用類型 :null。
匿名對象的應用
匿名對象時對象的簡化形式;
new 實例名稱();
使用方式一:當對對象的方法只調用一次時,可以用匿名對象來完成,這樣寫比較簡化,如果對一個 對象進行多個成員調用,必須給這個對象起個名字。
使用方式二:可以將匿名對象作爲參數使用
3,構造方法
構造器 又稱爲構造方法 ,constructor
構造器用於構造該類的實例。作用:用來初始化對象!
代碼:
[修飾符] 類名 (形式參數列表){
//語句
}
是一種特殊的方法:
(1)通過new關鍵字調用!!
(2)構造器雖然有返回值,但是不能定義返回類型(返回值的類型肯定是本類),不能再構造器裏調用 return。
(3)如果我們沒有定義構造器,則系統會自動定義一個無參數的構造函數,如果已經定義則編譯器不會添加,覆蓋了系統默認的構造方法。
(4)構造器的方法名必須和類名一致。
(5) 構造該類的對象,經常用來初始化對象的屬性。
1.構造方法必須與類名保持一致,
2.無返回類型
3.通過new來調用
4.無參構造函數問題:
a)如果我們沒有定義構造器,則系統會自動定義一個無參數的構造函數
b)如果已經定義則編譯器不會添加
5.構造方法的第一句總是super,即調用直接父類的構造方法
a)有繼承關係的構造方法調用的順序
對象一建立就會調用與之對應的構造函數,new幾次 就會調用幾次。
構造函數和一般方法在運行上的不同:
構造函數是在對象一建立就運行,給對象初始化。而一般方法是對象調用才執行,是給對象添加對象具備的功能
一個對象建立,構造函數只運行一次,而一般方法可以被該對象調用多次。
構造函數與set。get方法應同時存在,構造函數是給對象初始化,set、get方法是對外提供的公共方法。
什麼時候 定義構造函數?
分析事物時,該事物存在具備一些特性或者行爲,那麼將這些內容定義在構造函數中。
構造代碼塊:
{
語句
}
作用,給對象進行初始化,對象一建立就運行,而且優先於構造函數執行。
與構造函數的區別:
構造代碼塊是給所有對象進行統一初始化,定義的是不同對象共性的初始化內筒。構造函數是給對應的對象初始化。
4,this關鍵字
this關鍵字:隱式參數,用於區分成員和局部變量的同名情況。
在普通方法中,this總是指向調用該方法的對象。
在構造方法中,this總是指向正要初始化的對象。
this不能用於static方法中。還可以用來調用其他的構造方法。
this代表它所在函數所屬對象的引用,哪個對象在調用this所在的函數,this就代表哪個對象。
this關鍵字的應用。
在定義類中功能時,該函數內部要用到調用該函數的對象時,這時用this表示這個對象,但凡本類功能內部使用到了本類對象,都用this表示。
this關鍵字在構造函數間調用:
this();用於構造函數間的調用。
通過this調用構造方法,必須位於第一句。
5,static關鍵字
static 關鍵字
用法:是一個修飾符,只能用於修飾成員(成員變量和成員函數)
靜態變量 :
在類中,用static聲明的成員變量爲靜態成員變量,或者叫做類屬性、類變量。
-- 它爲該類的公用變量,屬於類,被該類的所有實例共享,在類被載入時,被顯示初始化。
-- 對於該類的所有對象來說,static成員變量只有一份,被該類的所有對象共享!!
-- 可以使用“對象.類屬性”來調用,不過一般都是用,類名.靜態成員
-- static變量位於方法區中。
靜態方法:
用static生命的方法爲靜態方法
-- 不需要對象,就可以調用
-- 在調用該方法時,不會講對象的引用傳遞給它,所以在static方法中不可訪問非static的成員。
特點:
1.隨着類的加載而加載 ,隨着類的消失而消失,生命週期最長
2.優先於對象存在
3.被所有對象所共享
4.可以直接被類名調用
實例變量和類變量的區別
1.存放位置:類變量隨着類的加載而存在於方法區中。
實例變量隨着對象的建立而尋在於堆內存中
2.生命週期
類變量生命週期最長,隨着類的消失而消失
實例變量生命週期隨着對象的消失而消失
靜態使用注意事項:
1.靜態方法只能訪問靜態成員,非靜態方法即可以訪問靜態也可以訪問非靜態
2.靜態方法中不可以定義 this super關鍵字,因爲靜態優先於對象存在,所以靜態方法中不可以出現this,靜態方法創建時,還沒有對象。
靜態的利於弊”
利:對對象的共享數據進行單獨空間的存儲,節省空間,沒有必要每一個對象中都存儲一份
可以被類名調用
弊:生命週期過長
訪問出現侷限性(靜態只能訪問靜態)
什麼時候使用靜態:
當對象中的出現共享數據時,該數據被靜態所修飾,對象中的特有數據要定義成非靜態尋在於堆內存中。
什麼時候定義靜態函數?
檔功能內部沒有訪問到非靜態數據(對象的特有數據),那麼該功能就能定義成靜態的,也就是說,該功能方法是否需要訪問非靜態的成員變量(共有的靜態變量)
如果不需要,就定義成靜態的。
靜態的應用:工具類,裏面的方法都是靜態方法。並且私有化構造函數。
每一個程序都有共性的功能,可以將這些功能進行抽取,獨立封裝,以便複用。
例子工具類:
class ArrayTool {
//私有化構造函數
private ArrayTool(){
}
//定義一個功能獲取數組中的最大值
public static int getmax(int[] arr){
int max=0;
for(int x=0; x<arr.length;x++){
if(arr[max]<arr[x]){
max=x;
}
}
return max;
}
//查詢某個值在數組中是否存在
public static int select(int[] arr,int key){
for(int x=0; x<arr.length;x++){
if(arr[x]==key){
return x;
}
}
return -1;
}
//選擇排序
public static void xuanze(int[] arr){
for(int x=0; x<arr.length-1;x++){
for(int y=x;y<arr.length;y++)
{
if(arr[x]>arr[y]){
int temp=arr[y];
arr[y]=arr[x];
arr[x]=temp;
}
}
}
}
//冒泡排序
public static void maopo(int[] arr){
for(int x=0; x<arr.length-1;x++){//控制比較的圈數
for(int y=0;y<arr.length-x-1;y++)//控制每圈比較的次數
{
if(arr[y]>arr[y+1]){
int temp=arr[y+1];
arr[y+1]=arr[y];
arr[y]=temp;
}
}
}
}
//數組打印
public static void pring(int[] arr)
{
for(int x=0;x<arr.length;x++)
{
if(x!=arr.length-1)
System.out.print( arr[x]+",");
else
System.out.println( arr[x]);
}
}
}
靜態代碼塊:
格式
static
{
靜態代碼塊中的執行語句
}
特點:隨着類的加載而執行,只執行一次並優先於主函數。
對象的初始化過程:
Person p=new Person("張",20);
1.new person類之後,加載person.class文件加載到內存中
2.執行該類中的static代碼塊,對Person.class進行初始化
3.在堆內存中開闢空間,分配內存地址
4.在堆內存中建立對象的特有屬性,並對其默認初始化
5.對特定屬性進行顯示初始化
6.對對象進行構造代碼塊初始化
7.進行構造函數初始化
8.將內存地址付給棧中的對象變量。
執行優先級
靜態代碼塊初始化----默認初始化------顯示初始化------構造代碼塊初始化----構造函數初始化
6,單例設計模式
單例設計模式:解決一個類在內存中只有一個對象
多個程序使用統一配置信息對象時,需要保證該對對象的唯一性。
保證對象唯一性的實現步驟:
1.將構造函數私有化:爲了避免其他程序過多建立該對象,禁止其他程序調用該對象。
2.在類中創建一個本類對象:爲了讓其他程序可以訪問該類對象
3.提供一個方法可以獲取到該對象的方法:方便其他程序訪問自定義的本類對象。
餓漢式:一進內存 就創建了對象
class single{
private single(){}1.將構造函數私有化
private static single s=new single();2.在類中創建一個本類對象
public static single getInstance(){3.提供一個方法可以獲取到該對象的方法
return s;
}
}
public class SingleDemo {
public static void main(String[] args) {
single ss=single.getInstance();
}
}
懶漢式:什麼時候調用什麼時候創建對象,對象的延時加載
class single{
private single(){}
private static single s=null;
public static single getInstance(){
if(s==null){
synchronized(Single.class)//同步鎖
{
if (s==null){
s=single.getInstance();
}
}
}
return s;
}
}
public class SingleDemo {
public static void main(String[] args) {
single ss=single.getInstance()
}
}
7,final關鍵字
final關鍵字
最終,作爲一個修飾符
1.可以修飾類類 函數 變量
2.被final修飾的類不可以被繼承,爲了避免被繼承,被子類複寫。
3.被final修飾的方法不能被複寫。
4.被final修飾的變量是一個常量只能賦值一次,既可以修飾成員變量,也可以修飾局部變量。
在描述一些事物時,一些數據的出現值是固定的,那麼爲了增強閱讀性,都給這些值起個名字,方便閱讀,而這個值不需要改變時,
加上final修飾,作爲常量。書寫範圍所有字母都大些,用_連接。
常量定義一般都是 public權限 、靜態的
5.內部類定義在類中的局部位置上時,只能訪問該局部被final修飾的局部變量
8,抽象類
抽象類:abstract。
當多個類中出現相同功能,但是功能主體不同,這時候可以進行向上抽取,只抽取功能定義,而不抽取功能主體。,分析事物不確定時,就抽象
特點
1.抽象方法只能定義在抽象類中。
2.抽象方法和抽象類必須被abstract修飾
3.抽象類不可以用new 創建對象,因爲創建對象沒有意義
4.抽象類中的抽象方法要被使用,必須由子類複寫抽象方法後,建立子類對象調用,
如果子類只覆蓋了部分抽象方法,那麼該子類還是一個抽象類。如果不想子類是抽象類必須,複寫父類抽象類中的全部抽象方法。
抽象類和一般類沒有太大的區別,當事物出現不確定的功能,需要明確指出,但是無法定義主體,通過抽象方法來表示。
抽象類比一般類多了個抽象方法,抽象類不可以實例化,抽象類中還可以定義非抽象方法,目的是不讓該類建立對象。
抽象類可以包含 普通方法 成員變量 構造方法
例子:
abstract class enmployee{
private String name;
private int id;
private double money;
public enmployee(String name, int id, double money) {
super();
this.name = name;
this.id = id;
this.money = money;
}
public abstract void work();
}
class yuangong extends enmployee{
public yuangong(String name, int id, double money) {
super(name, id, money);
}
public void work(){
//員工工作
}
}
class jingli extends enmployee{
double jiangjin;
public jingli(String name, int id, double money,double jiangjin) {
super(name, id, money);
this.jiangjin=jiangjin;
}
public void work(){
//經理工作
}
}
模板方法模式:在定義功能時,功能的一部分是確定的,但有一部分是不確定的,而確定的部分
在使用不確定的部分。就將不確定的部分暴露出去。由該來的子類去實現。將確定的部分final修飾 ,禁止修改。
案例:
/*
* 獲取一段程序的運行時間
*
*/
abstract class GetTime
{
public final void gettime()
{
long start = System.currentTimeMillis();
runcode();
long end = System.currentTimeMillis();
System.out.println(end-start);
}
public abstract void runcode();
}
class Subtime extends GetTime{
public void runcode(){
for(int x=0;x<1000;x++){
System.out.println(x);
}
}
}
9,接口
接口:interface,如果抽象類中的方法都是抽象的,那麼該類可以通過接口的形式來表示。接口定義的一組規範,
實現現實世界中這樣的邏輯 :如果你是。。。則必須能。。。
class用於定義類,interface用於定義接口
接口的特點:
1.接口中常見定義:常量,抽象方法。
2.接口中的成員都有固定修飾符
常量 public static final
方法 public abstract
接口中的成員都是public的。
接口是不可以創建對象的,因爲有抽象方法,需要被子類實現,子類對接口中的抽象方法全都覆蓋後,子類纔可以實例化,否則子類是一個抽象類。
接口可以被類多實現。也是對。多集成不支持的轉換形式,JAVA支持多實現。
類與類之間是繼承,只能單繼承
類與接口之間是實現,可以多實現
接口與接口之間是繼承,可以多繼承。
接口:藉口是對外暴露的規則,藉口是程序的工恩那個擴展,接口可以原來多實現,類與接口之間是實現關係,而且類的消失可以繼承一個類的同時
實現多個接口,接口與接口之間可以有繼承關係。
基本功能定義在類中,擴展功能定義在接口中。
10、面向對象三大特徵: 封裝 繼承 多態
繼承:
1.提高了代碼的複用性
2.讓類與類之間產生了關係,有了這個關係,纔有了多態的特性。必須是類與類之間有所屬關係纔可以使用繼承。所屬關係 is a。
java只支持單繼承,不支持多繼承,多繼承容易帶來安全隱患:當多個父類中定義了多個相同功能,當功能內容不相同時,不確定調用了父類的哪一個。
但是java可以通過多實現來實現。
Java同時支持多層繼承。
先要使用繼承體系,先要查閱父類的功能,因爲父類中定義的是該體系中的共性功能,通過了解共性功能,就知道該體系的基本功能。
調用時要創建最子類的對象,原因是:
1.因爲有可能父類不能創建對象
2.創建子類對象可以使用更多功能,包括基本的也包括特有的。
子父類出現後類成員的特點:
1.子父類中變量的特點:
父類的成員變量私有化之後,子類依然繼承,但不能直接訪問。
如果子類中出現非私有的同名成員變量時,要訪問本類中的變量用this,要訪問父類中的同名變量,用super,
super的使用和this的使用幾乎一致。
this:本類對象的引用
super:父類對象的引用。一定要放在子類函數的第一行。
2.子父類中函數的特點:--覆蓋(override)
當子類中出現和父類一模一樣的函數時,當子類對象調用該函數,會運行子類函數的內容,如同父類的函數被覆蓋一樣。這就是重寫。
當子類繼承了父類,沿襲了父類的功能,到子類中,但是子類雖然具備該功能,但是功能的內容卻和父類不一樣,這是沒有必要定義新
功能,而是使用覆蓋特性,保留父類的功能定義,並重寫功能內容。這是用 super。
注意:
1).子類覆蓋父類,必須保證子類權限大於等於父類權限,才能重寫
2).靜態只能訪問靜態
重載和重寫:
重載:只看同名函數的參數列表
重寫:字符類方法要一模一樣。
3.子父類中構造函數的特點:
在對子類對象進行初始化時,父類的構造函數也會運行,那是因爲子類的構造函數。子類只會隱式調用父類的空參構造函數。
子類一定要訪問父類中的構造函數:
因爲父類中的數據子類可以直接獲取,所以子類對象在建立時,需要先查看父類是如何對這些數據進行初始化的。所以子類在對象初始化時,要先訪問一下父類中的構造函數。如果要訪問父類中指定的構造函數,可以通過手動定義super語句的方式來指定。
注意:super語句一定定義在子類構造函數的第一行。
子類的所有構造函數,默認都會訪問父類中孔參數的構造函數,因爲子類的每一個構造函數內的第一行都有一句隱式super();
當父類中沒有空參數的構造函數時,子類必須手動通過super語句形式來指定要訪問父類中的構造函數。
當然子類的構造函數第一行也可以手動指定this語句來訪問本類中的構造函數,子類中至少會有一個構造函數會訪問父類中的構造函數。
多態
:可以理解爲事物存在的多種體現形態。
父類的引用指向子類的對象。
1.多態的體現
父類的引用指向子類的對象,父類的引用接收子類的對象。
2.多態的前提
類與類之間必須是繼承或者實現的關係。通常還有一個前提 就是覆蓋
3.多態的好處
大大的提高了程序的擴展性
4.多態的弊端
提高了擴展性,但是隻能使用父類的引用訪問父類中的成員。
5.多態的應用
6.多態中成員的特點
a)非靜態成員函數:在編譯時期,參閱引用型變量所屬的類中是否有調用的方法。如果有,編譯通過,如果沒有編譯失敗。
在運行時期,參閱對象所屬的類中是否有調用的方法。
總結,成員函數在多態調用的時候,編譯看左邊父類,運行看右邊子類。
b)成員變量:無論編譯和運行,都參考左邊父類。
c)靜態成員函數:無論編譯和運行都參考左邊父類。
如何使用子類特有方法:多態轉型。
Animal a=new Cat();//類型提升,向上轉型。
a.eat();//父類的共有方法。
如果要調用cat的特有方法的時候 ,就必須將父類的引用轉成子類的類型,但是不能將父類對象轉成子類類型,轉換的是父類的引用,(就是父類的變量)
Cat c=(Cat)a;
c.catchmouse();//子類的特有方法。
事例:
/*
* 基礎班學生:
* 學習 、睡覺
* 高級班學生
* 學習、睡覺
*
*/
abstract class Student {
public abstract void study();
public static void sleeps(){
System.out.print("睡覺");
}
}
class jichu extends Student{
public void study()
{
System.out.print("學習基礎");
}
public static void sleeps(){
System.out.print("站着");
}
}
class gaoji extends Student{
public void study()
{
System.out.print("學習高級");
}
public static void sleeps(){
System.out.print("趴着");
}
}
class DoStudent
{
public void dosome(Student stu)
{
stu.study();
stu.sleeps();
}
}
public class duotai {
public static void main(String[] args) {
DoStudent ds=new DoStudent();
ds.dosome(new jichu());//傳的是子類對象
ds.dosome(new gaoji());
}
}
.多態事例與拓展事例
需求:電腦運行實例
電腦運行基於主板。
/*
* 需求;主板事例
* 電腦運行實例,電腦運行基於主板
*
*/
interface PCI{
public void open();
public void close();
}
class MainBoard {
public void run(){
System.out.println("主板運行");
}
public void usePCI(PCI p){
if(p!=null){
p.open();
p.close();
}
}
}
class netcard implements PCI{
public void open(){
System.out.println("網卡運行");
}
public void close(){
System.out.print("網卡關閉");
}
}
public class duotai {
public static void main(String[] args) {
MainBoard mb=new MainBoard();
mb.run();
mb.usePCI(new netcard());
}
}
11,內部類
內部類
講一個雷定義在另一個類的裏面,對立面那個類就叫做內部類,或者嵌套類。
特點,內部類可以直接訪問外部類中的成員。包括私有成員。而外部類要訪問內部類中的成員必須要建立內部類對象。
代碼
class outer
class inner
void function(){}
}
}
inner內部類。
訪問規則:1.內部類可以直接訪問外部類中的成員,包括私有。是因爲內部類中持有了一個外部類的引用。格式:外部類名.this
2.外部類要訪問內部類,必須要建立內部類的對象。
3.內部類作爲外部類的成員 可以被私有化
如何直接訪問內部類中的成員 function()
outer.inner in=new outer().inner();
in.function();
訪問格式:
1.當內部類定義在外部類的位置上的時候,而且非私有,可以在外部其他類中,可以直接建立內部類對象
格式 外部類名.內部類名 變量名=外部類對象.內部類對象
outer.inner in=new outer().inner();
2.當內部類在成員的位置上,就可以被成員修飾符所修飾。
比如:private static,
當內部類被static修飾後,只能直接訪問外部類中的static成員,出現了訪問侷限
在外部其他類中,如何直接訪問static內部類的非靜態成員?
new Outer.Inner().sunction();
在外部其他類中,如何直接訪問staic內部類的靜態成員?
outer.inner.function();
注意:當內部類中定義了靜態成員,該內部類必須是static 的。
當外部類中的靜態方法訪問內部類是,該內部類必須是靜態的
內部類定義原則:
當描述事物中,事物的內部還有事物,該事物用內部類來描述。
因爲內部事務在使用外部事物中的內容。
局部內部類
class outer{
int x=3;
void method()
{
int y=4;
class inner{
void function(){}
}
}
}
內部類定義在局部的時候:
1.不可以被成員修飾符修飾
2.可以直接訪問外部類中的成員,因爲還持有外部類中的引用
但是不可以訪問它所在的局部中的變量,只能訪問被final修飾的局部變量。
方法中的內部類能不能訪問方法中的局部變量,爲什麼?
/**
*說明下此程序在內存中的執行過程,就可以瞭解 爲什麼方法中的內部類不能訪問方法中的局部變量。
*首先JVM找到主函數的入口, 在棧中就開闢了一個main方法的空間 同時 創建了一個 變量o,同時在堆內存中
*new Outer()分配了一塊內存空間,然後將變量O的引用就指向了該內存空間。Outer o=new Outer();
*這就話就執行完畢,在調用function的方法時,就會將方法區中的funtion()方法進入棧中,同時將x=4
*加載進棧內存中。如果不將x設置爲final ,執行完, o.function();就會將function方法彈棧,此時局部x也就不存在,
*內部了也是一個類,在創建一個改局部類的對象之後,只有沒有其他變量引用它,它纔會變成垃圾,並且在不定時消失
*。所以可能發生的情況是 :在局部變量消失之後,內部類的對象還存活。也就是說在執行 內部類add()方法的時候x已經
*不存在了。所以,方法中的內部類不能訪問方法中的局部變量。
*
*解決辦法 就是將x 前加final 變成該內部類inner對象中的一個數據成員。這樣,即使棧中局部變量x已消失,
*但由於它是final,其值永不變,因而局部內部類對象在變量x死亡後,照樣可以訪問final型局部變量。
*/
class Outer{
void function()
{
final int x=4;//方法的局部變量
//內部類
class Inner{
//內部類的方法
void add()
{
System.out.print(x+4); //調用外部類方法中的局部變量
}
}
new Inner().add();//創建個內部類的對象
}
}
public class Test2 {
public static void main(String[] args) {
Outer o=new Outer();//創建外部類對象並將 o的引用指向該變量的地址
o.function();//
}
}
12,程序執行過程內存分析
JAVA語言中除基本類型之外的變量類型,都稱之爲引用類型。
JAVA中對象是通過引用reference對其操作的。
新建對象時,引用類型的數據都是null,基本數據類型 int 是 0 string 是null boolean 是false (默認初始化)
方法:參數傳遞的值是 值傳遞。
局部變量 必須初始化,成員變量會被系統默認初始化。
棧:自動分配連續的空間,後進先出,一般用來放置局部變量,數據使用完畢之後,會自動釋放
堆:不連續,用來放置 new出來的對象,堆中的數據都會有默認初始化值。引用類型的數據都是null,基本數據類型 int 是 0 string 是null boolean 是false (默認初始化)
方法區:屬於堆的一部分,存放類的信息(代碼)、static變量、常量池(字符串常量)等
解析:
1、主函數中定義一個 int x=3;
主函數加載時,主函數就在內存中的棧中 開闢一個空間,此空間中就會定義一個變量 ,並且賦值爲3.
2、主函數中創建一個 int[] x=new int[3];
左邊 int[] x,在棧內存中定義了一個變量 x,
new int[3],在堆內存中開闢了一個空間,每個在堆內存中存在的數據都會有一個地址值。
= 時,就將x的引用指向了new 對象在堆內存中的地址值。
注:操作對象就是操作地址。
例子:
//通過類加載器 Class LOader加載Student類,加載後,在方法區中就有了Student中的類的信息
Student s1=new Student();
s1.name="張三";