單例定義,寫法
單例模式: 在程序運(yùn)行其間,保存類對(duì)象只會(huì)創(chuàng)建一次(確保只有一個(gè)實(shí)例)
實(shí)現(xiàn)步驟:
私有化構(gòu)造方法
提供本類的實(shí)例對(duì)象(好多種)
向類的外部提供一個(gè)方法,獲取類的對(duì)象
為什么使用單例:1.能避免實(shí)例重復(fù)創(chuàng)建 2.應(yīng)用于避免存在多個(gè)實(shí)例引起程序邏輯錯(cuò)誤的場(chǎng)合3.較節(jié)約內(nèi)存
單例寫法案例
public class Demo{
public static void main(String[] args) {
Singleton tv1 = Singleton.getInstance2("張三");
Singleton tv2 = Singleton.getInstance2("李四");
System.out.println(tv1 == tv2);//true//比較的是內(nèi)存地址
tv2.speak();//張三
}
}
class Singleton {
String name;
//1.私有化構(gòu)造方法
private Singleton(String name) {
this.name = name;
}
//2.提供本類的對(duì)象
private static Singleton singleton1;
//寫法一:同步懶漢式,3.向外部提供可訪問的方法,并返回當(dāng)前類的對(duì)象
public synchronized Singleton getInstance1(String name) {
if (singleton1 == null) {
singleton1 = new Singleton(name);
}
return singleton1;
}
//寫法二:優(yōu)化后的懶漢式,方法一中的同步冗余,3.向外部提供可訪問的方法,并返回當(dāng)前類的對(duì)象
public static Singleton getInstance2(String name) {
if (singleton1 == null) {
synchronized (Singleton.class) {
if (singleton1 == null) {
singleton1 = new Singleton(name);
}
}
}
return singleton1;
}
////////////////////////////////////////////
//寫法三:餓漢式2.提供本類的對(duì)象
private static Singleton singleton3 = new Singleton("default");
//3.向外部提供可訪問的方法,并返回當(dāng)前類的對(duì)象
public static Singleton getInstance3() {
return singleton3;
}
////////////////////////////////////////////
//寫法四:內(nèi)部類
/**
* 類級(jí)的內(nèi)部類,也就是靜態(tài)的成員式內(nèi)部類,該內(nèi)部類的實(shí)例與外部類的實(shí)例沒有綁定關(guān)系,
* 而且只有被調(diào)用到才會(huì)裝載,從而實(shí)現(xiàn)了延遲加載
*
private static class SingletonHolder {
//2.提供本類的對(duì)象
private static final Singleton instance = new Singleton("default");
}
//3.向外部提供可訪問的方法,并返回當(dāng)前類的對(duì)象
public static Singleton getInstance4() {
return SingletonHolder.instance;
}
////////////////////////////////////////////
public void speak() {
System.out.println(name);
}
}
super 、this關(guān)鍵字
方法的重寫:首先存在繼承關(guān)系,然后方法名、參數(shù)、返回?cái)?shù)據(jù)類型都與父類中方法名和參數(shù)相同。父類的私有方法不能被重寫,靜態(tài)只能覆蓋靜態(tài)。
this和super的使用
- this
this 代有當(dāng)前類的引用,可以通過this.成員形式來訪問成員變量和方法,可以通過this() 來調(diào)用本類的其它構(gòu)造方法,但必須在第一行 - super
super:代表是父類的數(shù)據(jù)空間,并不是一個(gè)引用,因此沒有對(duì)象可以指向可以通過 super.父類成員來訪問父類的成員變量和成員方法;也可以通過super()來調(diào)用父類的構(gòu)造方法,如果父類不存在無參的構(gòu)造方法;在子類中必須調(diào)用super(),來指明初始化父類成員變量的構(gòu)造方法,而且必須在第一行 。
注: super() 和 this()不能同時(shí)出現(xiàn),this和super不能出現(xiàn)靜態(tài)成員方法中
案例
public class Demo {
public static void main(String[] args){
Parent parent=new Child(); //多態(tài)
parent.say("您好");//父類說...您好
//子類說...您好
parent.print(); //父類打印...
}
}
class Parent {
public void say(String msg) {
System.out.println("父類說..." + msg);
}
public static void print() {
System.out.println("父類打印...");
}
}
class Child extends Parent {
//重寫父類的方法可以擴(kuò)展功能
public void say(String msg) {
super.say(msg); //調(diào)用父類的成員方法
System.out.println("子類說..." + msg);
}
public static void print() { //重寫父類的靜態(tài)方法時(shí),只能以靜態(tài)的方式覆蓋
//super.print(); //this和super不能出現(xiàn)在靜態(tài)成員方法中
System.out.println("子類打印...");
}
}
final關(guān)鍵字
final(C++ const): 最終的,修飾類、成員方法、成員變量、局部變量。
注意:
1、final修飾的類,不能被繼承
2、final修飾的方法,不能被重寫
3、final修飾的成員變量(局部變量),不能被修改
4.String類就是final 修飾的類
final案例
public class Demo {
public static void main(String[] args) {
Circle c = new Circle(5);
System.out.println(String.format("%.2f", c.area()));
}
}
final class Test1 { //這是一個(gè)最終的類
}
/*
class C_Test extends Test1{ //不能繼承final類
}
*/
class Test2 {
public final void area() { //final修飾的方法不能被子類重寫
}
}
class B_Test2 extends Test2 {
final int a = 10;
public void area(final int w, final int h) {
//a=90;//變量不能被修改
//w=w*h;
final Test2 t2 = new Test2();
//final 修飾的引用不能再指向其它對(duì)象
//t2=new Test2(); //出錯(cuò)
}
}
//求圓的面積 s=PI*r*r
class Circle {
public static final double PI = 3.1415;
private int radius; //半徑
public Circle(int radius) {
this.radius = radius;
}
public double area() {
return PI * radius * radius;
}
}
抽象類 abstract class
abstract: 抽象的,包含抽象方法的類叫抽象類,abstract修飾的類;抽象類也是一個(gè)類,只不過沒有足夠的信息來描述某一事物行為的方法;特點(diǎn):
1、抽象類不能創(chuàng)建對(duì)象,因?yàn)槠渲邪宋磳?shí)現(xiàn)的抽象方法
2、繼承抽象類的子類,如果沒有實(shí)現(xiàn)抽象方法,則這個(gè)類也是抽象類
注意:
抽象類一定是父類,不然沒有存在的意義。
抽象類是有構(gòu)造方法,用于初始化類中的成員變量
- 抽象類和普通類區(qū)別
相同點(diǎn): 都是類,可以被繼承
不同點(diǎn):抽象類不可以創(chuàng)建對(duì)象,普通類可以創(chuàng)建對(duì)象;抽象類可以包含抽象方法,普通類不能包含抽象方法
另,abstract不能與以下關(guān)鍵字組合使用:
final: final修飾的類不能被繼承,abstract類必須要被繼承(不然沒意義)
static: 靜態(tài)方法可以直接通過類名被調(diào)用,抽象方法不能被調(diào)用
private: 私有方法不能被重寫,抽象方法必須被重寫
案例
public class Demo {
public static void main(String[] args) {
//Circle1 c=new Circle1(6); //不能創(chuàng)建抽象類的對(duì)象
Rectangle r=new Rectangle(8,10);
System.out.println(r.area());//80.0
}
}
abstract class Shape{
public abstract double area();
}
abstract class Circle1 extends Shape{
public static final double PI=3.14;
private int radius;
public Circle1(){}
public Circle1(int radius){ this.radius=radius;}
public abstract double area();
}
class Rectangle extends Shape{
private int width;
private int height;
public Rectangle(int width,int height){
this.width=width;
this.height=height;
}
public double area(){ //實(shí)現(xiàn)抽象方法
return width*height;
}
}
abstract class Test3{ //注:抽象類中可以不存在抽象方法
}
class C_Test3 extends Test3{
}
interface 接口
-
定義格式
interface 接口名{ 全局常量; 抽象方法; } -
特點(diǎn)
1、 接口可以實(shí)現(xiàn)多繼承2、 接口主要用于被實(shí)現(xiàn),接口中的所有方法,在子類中必須全部實(shí)現(xiàn)
3、在定義接口實(shí)現(xiàn)類的時(shí),使用implements關(guān)鍵字,而且可以多實(shí)現(xiàn)
擴(kuò)展
類和類是繼承關(guān)系;類和接口是實(shí)現(xiàn)關(guān)系;通過繼承可以得到繼承體系統(tǒng)中基本功能;通過實(shí)現(xiàn)可以得到除繼承之外的額外功能;注: 一個(gè)可以存在繼承關(guān)系同時(shí)也可以存實(shí)現(xiàn)關(guān)系
案例
public class Demo{
public static void main(String[] args) {
System.out.println(AllAnimalListener.type); //可以通過接口名直接訪問全局變量//動(dòng)物
//AllAnimalListener all=new AllAnimalListener(); //接口不能創(chuàng)建對(duì)象
Listener listener = new Animal();
listener.walk();//動(dòng)物在走...
AllAnimalListener animalListener = new Animal();
animalListener.run();//最佳最近調(diào)用原則:多態(tài)性中體現(xiàn)//動(dòng)物在跑...
}
}
interface Listener {
//聲明全局常量
void walk();
}
interface CatListener {
void talk();
}
//接口的多繼承,因?yàn)樗薪涌谥蟹椒ǘ紱]有實(shí)現(xiàn),不會(huì)存在調(diào)用的不確定性問題
interface AllAnimalListener extends CatListener, Listener {
static final String type = "動(dòng)物";
void run();
}
class Animal implements AllAnimalListener, CatListener, Listener { //一個(gè)類可實(shí)現(xiàn)多個(gè)接口
//必須要實(shí)現(xiàn)或重寫接口的方法 CatListener
@Override
public void talk() {
System.out.println(type + "在說話...");
}
//必須要實(shí)現(xiàn)或重寫接口的方法 Listener
@Override
public void walk() {
System.out.println(type + "在走...");
}
//必須要實(shí)現(xiàn)或重寫接口的方法 AllAnimalListener
@Override
public void run() {
System.out.println(type + "在跑...");
}
}
Java多態(tài)
對(duì)象的多態(tài)性: 多種形態(tài),父類類型的引用指向子類對(duì)象,多態(tài)的前提:存在繼承或?qū)崿F(xiàn)關(guān)系
class 動(dòng)物{
public void eat(){}
}
class 貓 extends 動(dòng)物{
}
常態(tài): 貓看成是貓 貓 c=new 貓();
多態(tài): 貓是動(dòng)物 動(dòng)物 d=new 貓(); //第一種方式體現(xiàn)多態(tài)
void 方法名(動(dòng)物 d){ //第二種方式體現(xiàn)多態(tài)
d.eat();
}
動(dòng)物 方法名(int type){ //第三種方式體現(xiàn)多態(tài)
if(type==1){
return new 貓();
}
return new 狗();
}
多態(tài)的弊端
只能使用父類中定義的方法,并且子類必須重寫父類中的方法-
多態(tài)的好處
1.有繼承或者實(shí)現(xiàn)接口的關(guān)系2.重寫或者實(shí)現(xiàn)父類(接口)的方法
3.父類指針指向子類對(duì)象
多態(tài)案例
public class Demo {
public static void main(String[] args) {
Cat cat = new Cat();//常態(tài)
cat.eat();//貓吃魚
cat.catchMouse();//貓抓老鼠
Animal1 a1 = new Cat(); //多態(tài),第一種方式
//a1.eat();
//a1.catchMouse();//出錯(cuò),因?yàn)楦割愔袥]有聲明此方法
Animal1 a2 = new Dog();//多態(tài)
//a2.eat();
eat(a1);//貓吃魚
//小貓正在吃。。。。
eat(a2);//狗吃骨頭
//小狗正在吃。。。。
}
public static void eat(Animal1 animal) { //第二種方式體現(xiàn)多態(tài)性
animal.eat(); //調(diào)用是Animal實(shí)際對(duì)象的方法,實(shí)際對(duì)象可能是Cat、Dog的類對(duì)象
//通過instanceof關(guān)鍵字判斷對(duì)象是哪一種類型的對(duì)象
if (animal instanceof Cat) {
System.out.println("小貓正在吃。。。。");
} else {
System.out.println("小狗正在吃。。。。");
}
}
}
abstract class Animal1 {
public abstract void eat(); //虛方法
}
class Cat extends Animal1 {
public void eat() {
System.out.println("貓吃魚");
}
public void catchMouse() {
System.out.println("貓抓老鼠");
}
}
class Dog extends Animal1 {
public void eat() {
System.out.println("狗吃骨頭");
}
public void kanjia() {
System.out.println("看家");
}
}
向上,向下轉(zhuǎn)型
多態(tài)第三種體現(xiàn)方式,向上轉(zhuǎn)型: 子類類型向父類類型轉(zhuǎn)換(自動(dòng)--多態(tài)的體現(xiàn));向下轉(zhuǎn)型: 當(dāng)子類中擴(kuò)展的方法被調(diào)用時(shí),需要將對(duì)象轉(zhuǎn)成子類類型對(duì)象
案例
/**
* 注意: 為了減少錯(cuò)誤,在強(qiáng)轉(zhuǎn)之前,可以通過instanceof判斷對(duì)象是否為某一種類型
*/
public class Demo{
public static void main(String[] args) {
Animal2 a1 = new Cat1(); //向上轉(zhuǎn)換,子類對(duì)象轉(zhuǎn)成父類對(duì)象
eat(a1);//貓吃魚
//小貓正在吃。。。。
// 貓抓老鼠
Animal2 a2 = newAnimal(Animal2.TYPE_DOG);
eat(a2);//狗吃骨頭
//小狗正在吃。。。。
//看家
}
public static void eat(Animal2 animal) { //第二種方式體現(xiàn)多態(tài)性
animal.eat(); //調(diào)用是Animal實(shí)際對(duì)象的方法,實(shí)際對(duì)象可能是Cat、Dog的類對(duì)象
//通過instanceof關(guān)鍵字判斷對(duì)象是哪一種類型的對(duì)象
if (animal instanceof Cat1) {
System.out.println("小貓正在吃。。。。");
//需要調(diào)用Cat中擴(kuò)展的方法
Cat1 c = (Cat1) animal; //向下轉(zhuǎn)型,父類對(duì)象向子類對(duì)象轉(zhuǎn)型
c.catchMouse();
} else {
System.out.println("小狗正在吃。。。。");
Dog1 d = (Dog1) animal;
d.kanjia();
}
}
//第三種體現(xiàn)多態(tài)性,根據(jù)類型創(chuàng)建某一動(dòng)物的對(duì)象
public static Animal2 newAnimal(int type) {
if (type == Animal2.TYPE_CAT) {
return new Cat1();
} else if (type == Animal2.TYPE_DOG) {
return new Dog1();
}
return null;
}
}
abstract class Animal2 {
public static final int TYPE_CAT = 1;
public static final int TYPE_DOG = 2;
public abstract void eat(); //虛方法
}
class Cat1 extends Animal2 {
public void eat() {
System.out.println("貓吃魚");
}
public void catchMouse() {
System.out.println("貓抓老鼠");
}
}
class Dog1 extends Animal2 {
public void eat() {
System.out.println("狗吃骨頭");
}
public void kanjia() {
System.out.println("看家");
}
}
接口的多態(tài)體現(xiàn)
接口是一種引用數(shù)據(jù)類型,定義接口的引用指向到接口實(shí)現(xiàn)類對(duì)象, 則是接口體現(xiàn)多態(tài)的方式
/**
* 接口中的多態(tài)
* 接口與多態(tài)
*
*/
public class Demo {
public static void main(String[] args) {
Listen l1 = new Animal3(); //接口的多態(tài)
l1.walk();//動(dòng)物在走
l1.music();//動(dòng)物在唱歌
Listen l2 = new Person();
l2.walk();//人在走
l2.music();// 人在唱歌
}
}
interface Listen {
void music();
void walk();
}
class Animal3 implements Listen {
@Override
public void music() {
System.out.println("動(dòng)物在唱歌");
}
@Override
public void walk() {
System.out.println("動(dòng)物在走");
}
}
class Person implements Listen {
@Override
public void music() {
System.out.println("人在唱歌");
}
@Override
public void walk() {
System.out.println("人在走");
}
}
多態(tài)中的調(diào)用
-
多態(tài)中成員特點(diǎn)
成員變量: 能訪問哪些成員變量,編譯和運(yùn)行時(shí)都看父類
成員方法: 訪問哪些方法,編譯時(shí)看父類,其運(yùn)行結(jié)果要看子類
靜態(tài)成員: 都看父類
案例
public class Demo{
public static void main(String[] args) {
Parent1 parent1 = new Child1();
System.out.println("num->" + parent1.num); //成員變量的結(jié)果:在編譯和運(yùn)行都看父類//num->20
parent1.say();//運(yùn)行哪一個(gè)方法,編譯時(shí)看父類,運(yùn)行時(shí)看子類//Child1 say()
parent1.fun();//運(yùn)行哪一個(gè)方法:(靜態(tài))編譯和運(yùn)行時(shí)都看父類//Parent1 static fun()
Child1 child1 = (Child1) parent1;
System.out.println("num->" + child1.num);//num->50
child1.say();//Child1 say()
child1.fun();//Child1 static fun()
}
}
class Parent1 {
int num = 20;
public void say() {
System.out.println("Parent1 say()");
}
public static void fun() {
System.out.println("Parent1 static fun()");
}
}
class Child1 extends Parent1 {
int num = 50;
public void say() {
System.out.println("Child1 say()");
}
public static void fun() {
System.out.println("Child1 static fun()");
}
}