Java学习第七项--类和对象

本文详细讲解了Java编程中的对象、引用、构造方法、继承、类属性(静态属性)、方法重载、传参、访问修饰符和单例模式等基础知识。通过实例展示了如何创建对象、使用引用、调用构造方法以及如何设计类的继承结构。此外,还讨论了静态属性和方法的特性,以及单例模式在确保类只有一个实例时的应用。文章最后提到了枚举类型在定义常量时的优势,并给出了相关代码示例。
摘要由CSDN通过智能技术生成

一、引用

1、引用和指向

引用的概念,如果一个变量的类型是 类类型,而非基本类型,那么该变量又叫做引用

 new Hero();

这里表示创建了一个新的Hero对象,但也仅仅是创建了一个对象而已,并不能访问。
若需要访问该对象,还需要引用来代表该对象

 Hero h = new Hero();

h变量是Hero类型,代表这个对象,也就是引用,

"="是 左边的h代表右边创建的这个Hero对象
“代表”在面向对象里,叫做“指向”。

public class TEST_20221128 {
        //创建类时,需要有属性。
        String name;
        float hp;
        float armor;
        int moveSpeed;
        public static void main(String[] args) {
            new TEST_20221128();
            TEST_20221128 h = new TEST_20221128();
        
    }

}

 单独声明这一行,其实可以不用。

2、多个引用,一个对象

引用可以有多个,但是对象只能有一个。

同一个创建的对象,可以被多个引用表示。就像房子和房产证。

房子只有一个,但房产证的复印件可以有很多。

public class TEST_20221128 {
        //创建类时,需要有属性。
        String name;
        float hp;
        float armor;
        int moveSpeed;
        public static void main(String[] args) {
            new TEST_20221128();
            TEST_20221128 h = new TEST_20221128();
            TEST_20221128 h1 = h;
            TEST_20221128 h2 = h;
            TEST_20221128 h3 = h1;
            

    }

}

 3、一个引用,多个对象

当一个引用,指向了两个对象时,将失去他所指向的第一个对象,相当于被覆盖住了。

一个引用同一时间只允许指向一个对象。

第8行,引用garen指向新创建的对象(对象1)
第9行,同一个引用garen指向新创建的对象(对象2)
这个时候,对象1,就没有任何引用指向了

package charactor;
 
public class Hero {
    public String name;
    protected float hp;
 
    public static void main(String[] args) {
           Hero garen =  new Hero();//8
           garen =  new Hero();//9
    }
}

这里在第9行没有将garen再标注前缀类型Hero,是因为第8行已经标注过,系统会默认其类型为Hero。

 二、继承

在LOL中,武器是物品的一种,也是有名称和价格的
所以在设计类的时候,可以让武器继承物品,从而继承名称和价格属性

1、物品类Item

声明两个属性name和price

 class Item{
    String name;
    int price;
}

2、武器类Weapon(但是不继承Item)

class Weapon{//不继承Item
    String name;
    int price;
    int damage;
}

3、武器类Weapon1(继承Item)

class Weapon1 extends Item{//继承Item
    int damage;

    public static void main(String[] args) {
        Weapon1 infinityEdge = new Weapon1();
        infinityEdge.damage = 65;//damage 是 Weapon1中新声明的
        infinityEdge.name = "无尽之刃";
        infinityEdge.price = 3600;//此时的name 和 price 是之前Item声明的。被继承了。
        System.out.println(infinityEdge.name);
    }
}

可以看到,虽然Weapon1没有声明name和price,但是继承了物品类

class Weapon1 extends Item{}

因此也可以使用物品类的属性。

4、练习

设计一个类Armor护甲
继承Item类,并且额外提供一个属性ac: 护甲等级 int类型

实例化出两件护甲
名称    价格    护甲等级
布甲    300     15
锁子甲 500     40

class Armor extends Item{
    int ac;

    public static void main(String[] args) {
        Armor pijia = new Armor();
        pijia.name = "布甲";
        pijia.price = 300;
        pijia.ac = 15;

        Armor hujia = new Armor();
        hujia.name = "锁子甲";
        hujia.price = 500;
        hujia.ac = 40;

        System.out.println(hujia.ac);

    }
}

三、方法的重载

方法的重载指的是 方法名一样,但是方法的参数不一样

1、attack方法的重载

有一种英雄,叫做物理攻击英雄 ADHero
为ADHero 提供三种方法

public void attack()
public void attack(Hero h1)
public void attack(Hero h1, Hero h2)

方法名是一样的,但是参数类型不一样
在调用方法attack的时候,会根据传递的参数类型以及数量自动调用对应的方法

class ADHero{
    String name;
public void attack(){
    System.out.println(name + "打了一下啊,但不知打的谁?");
}
public void attack(ADHero h1){
    System.out.println(name + "打了一下" + h1.name);
}
public void attack(ADHero h1,ADHero h2){
    System.out.println(name + "打了一下" + h1.name + "以及" + h2.name);
}
    public static void main(String[] args) {
        ADHero h = new ADHero();
        h.name = "皮城女警";
        ADHero h1 = new ADHero();
        h1.name = "金克斯";
        ADHero h2 = new ADHero();
        h2.name = "wei";

        h.attack();
        h.attack(h1);
        h.attack(h1,h2);
    }
}

这就体现了不同数量的参数下,系统会自动调用相应的的方法。

2、可变数量的方法重载

当需要多个参数时,我们应把每一种参数数量的方法都列举出来,这太麻烦了!

因此就出现了 可变数量的方法重载。只需设计一个方法,就可以囊括所有的数量。

public void attack(Hero ...heros)
public class Main {
    String name;
}
class ADHero extends Main{

public void attack(){
    System.out.println(name + "打了一下啊,但不知打的谁?");
}
public void attack(Main... main){
    for(int i = 0; i < main.length; i++ ){
        System.out.println(name + "打了" + main[i].name);
}
}
    public static void main(String[] args) {
        ADHero h = new ADHero();
        h.name = "皮城女警";
        Main h1 = new Main();
        h1.name = "金克斯";
        Main h2 = new Main();
        h2.name = "wei";

        h.attack();
        h.attack(h1);
        h.attack(h1,h2);
    }
}

其中需要注意的attack(Main... main),括号里的内容为 Main:类类型,也就是使用方法时,括号里的参数的类型(h1等)。
main 只是定义的一个参数名,不重要,可以修改。

for循环,当出现的参数很多时,,判断长度,自动调用。

3、练习

设计一个类Support (辅助英雄)继承Hero,提供一个heal(治疗)方法
对Support的heal方法进行重载
heal()
heal(Hero h) //为指定的英雄加血
heal(Hero h, int hp) //为指定的英雄加了hp的血

class Support extends Hero{
    public void help(){
        System.out.println(name + "疯狂的奶!");
    }
    public void help(Hero...hero){
        for (int i = 0; i < hero.length; i++)
            System.out.println(name + "奶了" + hero[i].name);
    }
    public void help(Hero h1, int hp){
        System.out.println(name + "奶了" + h1.name + hp + "的血量");
    }

    public static void main(String[] args) {
        Support h = new Support();
        h.name = "众星之子";
        h.help();
        Support h1 = new Support();
        h1.name = "大树";
        h.help(h1);
        h.help(h1,250);
    }
}

 四、构造方法

通过一个类创建一个对象,这个过程叫做实例化

实例化是通过调用构造方法(又叫做构造器)实现的

1、什么是构造方法

方法名类名一样(包括大小写)

没有返回类型

实例化一个对象时,必定会调用构造方法。

public class Hero {
    String name;
    float hp;
    float armor;
    int moveSpeed;
    //方法名和类名一样
    public Hero(){
        System.out.println("实例化一个对象时,必定调用构造方法!");
    }

    public static void main(String[] args) {
        Hero h = new Hero();//实例化

    }
}

2、隐式的构造方法

纯傻逼、

如果不提供构造方法,那么再实例化对象时(引用时),会默认有一个没有返回内容的构造方法。

3、如果提供了一个有参数构造方法

提供了有参数的构造方法

但是没有一个显示的(就是写出来的)构造方法

那么隐式的这个实例化就不成立。

public class Hero {
    String name;
    float hp;
    float armor;
    int moveSpeed;
    //方法名和类名一样
    public Hero(String name){
        System.out.println("实例化一个对象时,必定调用构造方法!");
    }

    public static void main(String[] args) {
        Hero h = new Hero("女警");//有参数实例化
        Hero h1 = new Hero();//隐式实例化

    }
}

 

此时无参数的实例化就不成立了

4、构造方法的重载

和普通方法一样,构造方法也可以重载

public Hero(){
       
   }
    public Hero(String name){
        System.out.println("实例化一个对象时,必定调用构造方法!");
    }public Hero(String name,int moveSpeed){
        System.out.println(name + "的运动速度为:" + moveSpeed);
    }

    public static void main(String[] args) {
        Hero h = new Hero("女警");//有参数实例化
        Hero h1 = new Hero("女警",250);//有参数实例化
       // Hero h1 = new Hero();//隐式实例化

在对构造方法重载时,务必要有无参数的构造方法,否则运行不了。

5、练习

为Hero设计4个参数的构造方法
这四个参数分别是
String heroName
float heroHP
float heroArmor
int heroMoveSpeed

 public Hero(String heroName, float heroHP, float heroArmor, int heroMoveSpeed){
       name =heroName;
       hp = heroHP;
       armor = heroArmor;
       moveSpeed = heroMoveSpeed;
    }


   public static void main(String[] args) {
      
        Hero h2 = new Hero("金克斯", 1001, 20, 251);//隐式实例化
        System.out.println(h2.moveSpeed);
    }

五、Jave里的this代表什么

this这个关键字,相当于普通话里的“我”

小明说 “我吃了” 这个时候,“我” 代表小明
小红说 “我吃了” 这个时候,“我” 代表小红
"我"代表当前人物

this这个关键字,相当于普通话里的“我”
this即代表当前对象

这里注意一点,在实例化一个对象时,会给对象分配一个随机地址。

1、this代表当前对象

public class Hero {
    String name;
    float hp;
    float armor;
    int moveSpeed;
    //方法名和类名一样
public void showAddress(){
    System.out.println("打印当前this看到的虚拟地址:" + this);
}
    public static void main(String[] args) {
    Hero garen = new Hero();
    garen.name = "盖伦";
        System.out.println("直接打印对象的地址:"+garen);
        garen.showAddress();
    }
}

此时的this就指代的是garen。所以其地址一样。

2、通过this访问对象属性

public class Hero {
    String name;
    float hp;
    float armor;
    int moveSpeed;
    //方法名和类名一样
    public void showAddress(){
     System.out.println("打印当前this看到的虚拟地址:" + this);
    }
    //参数名和属性名一样
    //在方法体中,只能访问到参数name
    public void setName1(String name){
        name = name;
    }
    //为避免setName1中的问题,参数名改变一下
    public void setName2(String heroName){
        name = heroName;
    }
    //通过this访问对象属性
    public void setName3(String name){
        //name代表的是参数name
        //this.name 代表的是属性 name
        this.name = name;
    }
    public static void main(String[] args) {
        Hero garen = new Hero();
        garen.setName1("盖伦");
        System.out.println(garen.name);
        garen.setName2("赵信");
        System.out.println(garen.name);
        garen.setName3("皇子");
        System.out.println(garen.name);
    }
}

输出的结果里,第一个输出为空值。
这是因为在setName1中直接使用了name = name ,这里意味着只是方法的参数等于参数,(也是因为属性名与参数名一致导致的)并没有对实例化的对象赋值。所以在访问第一个对象的名字时,还是为空。

setName2中的参数名与属性名不一致,成功赋值。

setName3this.name就代表了该对象,虽然参数名仍与属性名相同,但没有关系。这里还是将该对象的名字成功赋值。

3、用this调用其他的构造方法

在一个构造方法里用this调用其他的构造方法。

public class Hero {
    String name;
    float hp;
    float armor;
    int moveSpeed;


    public Hero(){

    }  
    //只有一个参数的构造方法
    public Hero(String name){
        System.out.println("只有一个参数的构造方法");
        this.name = name;
    }
 
    //两个参数的构造方法
    public Hero(String name, int hp){
        this(name);
        System.out.println("两个参数的构造方法");
        this.hp = hp;
    }
    public static void main(String[] args) {
        Hero h = new Hero("萨米拉");
        System.out.println(h.name);
        Hero h1 = new Hero("女坦",3600);
        System.out.println(h1.name + "的血量为" + h1.hp);
    }
}

this(name);   就是调用了只有一个参数时的构造函数。?????为啥啊

   this代表调用该构造函数的hh1 相当与 new Hero(name);

4、练习

参考练习-构造方法 设计一个构造方法,但是参数名称不太一样,分别是
String name
float hp
float armor
int moveSpeed

不仅如此,在这个构造方法中,调用这个构造方法

public Hero(String name,float hp)
public class Hero {
    String name;
    float hp;
    float armor;
    int moveSpeed;


    public Hero(){

    }
    //只有一个参数的构造方法
    public Hero(String name){
        System.out.println("只有一个参数的构造方法");
        this.name = name;
    }

    //两个参数的构造方法
    public Hero(String name, float hp){
        this(name);
        System.out.println("两个参数的构造方法");
        this.hp = hp;
    }
    public Hero(String name, float hp, float armor, int moveSpeed){
        this(name,hp);
        System.out.println("四个参数的构造方法");
        this.armor = armor;
        this.moveSpeed = moveSpeed;
    }
    public static void main(String[] args) {
        Hero h = new Hero("萨米拉");
        System.out.println(h.name);
        Hero h1 = new Hero("女坦",3600,150,350);
        System.out.println(h1.name + "的血量为" + h1.hp + ",护甲为:" +h1.armor);
    }
}


this(name,hp), 就是代表了调用有两个参数的构造函数,其中,Hero(name,hp)里再调用了只有一个参数的构造方法。

六、传参详解

变量有两种类型,

(1)基本类型

(2)类类型

所以传参也有两种:

(1)基本类型传参

(2)类类型传参

1、基本类型传参

 在方法内的参数修改,不会影响方法外(其他方法或者主函数的参数)

public class Hero {
    String name;
    float hp;
    float armor;
    int moveSpeed;


    public void huixie(int xp){
        hp = hp + xp;
        xp = 0;
    }


    public Hero(){
    }


    //两个参数的构造方法
    public Hero(String name, float hp){
        this.hp = hp;
        this.name = name;
    }

    public static void main(String[] args) {
    Hero h = new Hero("卢锡安",355);
    int xuePing = 150;
    h.huixie(xuePing);
        System.out.println(xuePing);
    }
}

虽然方法内显示,参数xp=0;但不修改方法外的参数,故xueping仍为150

2、引用 与 =

如果一个变量是基本类型
比如 int hp = 50;
我们就直接管hp叫变量
=表示赋值的意思。
如果一个变量是类类型
比如 Hero h = new Hero();
我们就管h叫做引用。
=不再是赋值的意思
=表示指向的意思
比如 Hero h = new Hero();
这句话的意思是
引用h,指向一个Hero对象

3、类类型传参

类类型又叫做 引用

Hero h = new Hero("卢锡安",355);

表示引用h,指向一个名字为卢锡安,血量为355的Hero对象。

 Hero h1 = new Hero("盖伦",616);

表示引用h1,指向一个名字为盖伦,血量为616的Hero对象。

public class Hero {
    String name;
    float hp;
    float armor;
    int moveSpeed;

    public Hero(){
    }
    //两个参数的构造方法
    public Hero(String name, float hp){
        this.hp = hp;
        this.name = name;
    }
    public void attack(Hero hero,int damage){
        hero.hp = hero.hp - damage;
    }
    public static void main(String[] args) {
        Hero h = new Hero("卢锡安",355);
        Hero h1 = new Hero("盖伦",616);
        h.attack(h1,100);
        System.out.println(h1.hp);
    }
}

再主函数里,

 h.attack(h1,100);

使得这两个引用h和h1,就指向了同一个对象。因此,改变了h1所指向的对象的血量。

4、练习

在方法中,使参数引用指向一个新的对象

外面的引用是指向原来的对象?还是新的对象?

public class Hero {
    String name;
    float hp;
    float armor;
    int moveSpeed;

    public Hero(){
    }
    //两个参数的构造方法
    public Hero(String name,float hp){
        this.name = name;
        this.hp = hp;
    }

    //复活
    public void revive(Hero h){
        h = new Hero("提莫",383);
    }
    /*public void revive1(Hero h){
        this.hp = 383;
    }*/

    public static void main(String[] args) {
        Hero teemo =  new Hero("提莫",383);

        //受到400伤害,挂了
        teemo.hp = teemo.hp - 400;

        teemo.revive(teemo);

       System.out.println(teemo.hp);

    }

}

此时的结果

 当我们在主函数了引用一个实体teemo指向一个对象时,此时是指向该对象的地址,在主函数对于该对象的修改,也会保存在该地址里。

但是,此时调用其他方法,且方法内引用了另一个实体h,指向一个对象,需注意此时这个对象是具有新的地址的,也就是与我们在主函数引用的teemo并不相同。所以对h的操作并不会体现在主函数输出中。

但是如果使用了this,那么就表示,作为参数的引用teemo的地址,传递给了这个方法。

此时在该方法里的修改也会体现在方法外。

public void revive1(Hero h){
        this.hp = 383;
    }

不使用this下,基本类型和类类型都不影响方法外的参数。

七、包 package

把比较接近的类。规划在同一个包里

1、规划相近的类

将Hero和ADhero 放在角色包charactor里

打开相应类时,会显示在哪个包下。

2、使用其他包的类,必须用import

当要使用其他包里的类时,需要使用importy

后面接上 包名.类名   或者(*   代表所有的类)

八、访问修饰符

成员变量有四种修饰符

private 私有的
package/friendly/default 不写(就是没啥意义)
public 公有的
protected 受保护的

例如私有成员变量

private String name;

1、类之间的关系

类之间的关系有以下几种:
以类|Hero为例:

自身:指的是Hero自己;
同包子类:ADHero是Hero的子类,且两个类在同一个包charactor下;
不同包子类:Support也是Hero的子类,但是不在同一个包下;
同包类:GiantDragon和Hero在同一个包下,但并没有继承关系;
其他类:Item类在其他包里。且无继承关系。

2、私有的private

自身:可以访问
同包子类:不能继承
不同包子类:不能继承
同包类:不能访问
其他类:不能访问

因为同包类和其他类本来就没有继承关系,所以只是不能访问。

设置了私有的成员变量

ADHero继承Hero,但是试图访问私有变量。

3、package/friendly/default 不写

没有修饰符的。
自身:可以访问
同包子类:可以继承
不同包子类:不能继承
同包类:可以访问
其他类:不能访问

 

 没写修饰符的,且在同一个包的可以选择继承或访问,但是不同包,就无法操作。

 试图从别的包下继承Hero,并访问;

package property;


import charactor.Hero;
public class Weapon extends Hero {
    float armor;
    public static void main(String[] args) {
        Weapon w = new Weapon();
        w.name = "无尽之刃";
        System.out.println(w.name);
    }
}

 失败

  同理,不同包子类也不可以。
4、protected 受保护的

 受保护的修饰符,

protected float hp; 血量 

自身:可以访问
同包子类:可以继承
不同包子类:可以继承
同包类:可以访问
其他类:不能访问

 子类都可以继承,同包类可以访问,其他类不访问。

5、public公共的

最宽松的修饰符。所有都可以操作。

6、总结

 7、什么时候该用什么?

什么时候该用什么修饰符呢??

由上图知,public有最大范围,可以应对所有情况,但是我们真的需要把所有变量方法都用上吗?

并不是的。

通常来讲:

(1)方法一般用public,保证都可以调用。
(2)属性一般使用private,约束起来。
(3)可以被子类继承的方法,使用protected。
(4)package用的很少,一般新手会用。

“作用范围最小原则”

能用 private就用,如果在编码过程中,需要调用时,再逐步放宽界限。

九、Java的类属性——静态属性

当一个属性被static修饰,就称他为类属性,也叫静态属性

当一个属性被声明为类属性时,所有创建的对象,其该属性共用同一个值。

普通的对象属性相比:

例如属性 int hp;
对于盖伦的hp和提莫的hp。使用不一样的值。
但对于类属性,就会使用一样的值。

1、类属性

类属性:静态属性

对象属性:实例属性,非静态属性
如果一个属性声明成类属性,那么所有的对象,都共享这么一个值
给英雄设置一个类属性叫做“版权" (copyright), 无论有多少个具体的英雄,所有的英雄的版权都属于 Riot Games公司。

 static String copyRight = "Riot";
Hero teemo =  new Hero("提莫",383);
System.out.println(teemo.copyRight);

虽然

没有在主函数里定义,但是却可以输出copyRight,且结果为Riot;

2、访问类属性

访问方法有两种:

(1)对象.类属性

teemo.copyRight

  (2)类.类属性

Hero.copyRight

推荐使用第二种方式。

3、什么时候使用什么属性?

当一个游戏,所有英雄都不一样,都有自己的名字name,此时就应该使用对象属性。

但如果所有英雄都一个属性一样,列如:等级上限18,此时就可以使用类属性,提前设置好,保证一致性。

4、练习

通过garen.copyright修改其值

并输出,观察是否发生改变。

 System.out.println("未修改的:" + Hero.copyRight);
        Hero.copyRight = "cjw";
        System.out.println("修改过的:" + Hero.copyRight);

 直接修改Hero.copyRight,所有对象的该属性值都会修改

    System.out.println("未修改的:" + teemo.copyRight);
        h1.copyRight = "cjw";
        System.out.println("修改过的:" + teemo.copyRight);

通过其他对象进行修改后,对于该属性,所有对象都会修改。

十、类方法

类方法:静态方法

对象方法:实例方法、非静态方法

访问一个对象的前提是,该对象已经被创建


访问类方法,并不需要对象的存在就可以访问。

1、类方法

如同上述介绍,

类方法除了修饰符外,还需要使用一个static。表示其为类方法

 public static void battle(){
        System.out.println("懒大王!!!");
    }


而对象方法没有

package charactor;

public class Hero {
    static String copyRight = "Riot";
    protected int id;
    String name;
    float hp;
    float armor;
    int moveSpeed;

    public Hero(){
    }
    //两个参数的构造方法
    public Hero(String name,float hp){
        this.name = name;
        this.hp = hp;
    }

    //复活
    public void revive(Hero h){
        h = new Hero("提莫",383);
    }
    public void revive1(Hero h){
        this.hp = 383;
    }
    //类方法
    public static void battle(){
        System.out.println("懒大王!!!");
    }
    public static void main(String[] args) {
        Hero teemo =  new Hero("提莫",383);
        Hero h1 = new Hero();
         teemo.battle();
    }

}

2、访问类方法

和之前的类属性一样 也有两个访问方法

(1)对象.类方法

garen.battleWin();

(2)类.类方法

Hero.battleWin();

推荐使用第二种。

例如生成随机数的 Math.random();

random()就是一个类方法,因为并不一定有Math这个对象实例。

3、什么时候设计对象方法,什么时

候设计类方法?

(1)如果方法里调用了对象属性,类如

    public String getName(){
    	return name;
    }

这个方法访问了对象属性name,对于每一个实例,都有不同的name,所以也要使用对象方法。

(2)如果没有调用任何对象属性,可以考虑设计为类方法例如:

  public static void battle(){
        System.out.println("懒大王!!!");
    }

4、练习

在类方法里调用对象方法,看能不能行不?

参考一下,使用this来代指当前。

  public void attack() {
        System.out.println(name + "打了一下啊,但不知打的谁?");
    }
    public static void battle(){
        this.attack();
        System.out.println("懒大王!!!");
    }

 但实际上不行,在类方法里调用对象方法不成立。

原理:

类方法是在类定义时进行分配和装载的,而对象方法是在对象实例化后,调用该对象方法时,才分配空间的。因此,对于没有实例化对象的类方法,对象方法就无法使用。

十一、属性初始化

1、对象属性初始化

有三种方式:

(1)声明对象属性时初始化
(2)构造方法中初始化
(3)初始化块

public class Hero {
    static String copyRight = "Riot"; //声明该属性时初始化
    protected int id;
    {
        id = 10001; //初始化块
    }
    public String name;
    float hp;
    float armor;
    int moveSpeed;

    public Hero(){
        this.name = "小瘪三";//构造方法中初始化
    }
 Hero h1 = new Hero();
 System.out.println(teemo.id+""+h1.name); 

可以看见提前初始化的结果都可以打印出来

2、类属性初始化

不同于对象属性初始化,只有两种。
(1)声明该类属性时初始化;
(2)静态初始化块;

static String copyRight = "Riot"; //声明该属性时初始化
    static {
        copyRight = "YSKM";  //静态初始化块
    }
 System.out.println("未修改的:" + teemo.copyRight);

 

 后初始化的值被输出。

十二、单例模式

LOL里只有一个大龙,GiantDragon,故只能别实例化一次。

1、单例模式

又叫Singleton模式,指的是一个类,在JVM里,只有一个实例存在。

2、饿汉式单例模式

GiantDragon应该只有一只,通过私有化其构造方法,使得外部无法通过new得到新的实例;

GiantDragon使用了一个public static的getInstance的方法,使得外部调用者可以使用该方法获取定义的唯一对象。而且每次获取的都是用一个对象。

package charactor;

public class GiantDragon {
    private  GiantDragon(){
        //私有化 构造方法 , 使得该类无法在外部通过new进行实例化
    }
        //准备一个私有的类属性,指向一个实例化对象。因为是类属性(静态属性),所以只有一个。
    private static GiantDragon instance = new GiantDragon();
    public static GiantDragon getInstance(){
        return instance;
    }
}


 如果在外部想实例化对象会失败。

package charactor;

public class TestGiantDragon {
    public static void main(String[] args) {
        //外部实例化不通过
        //GiantDragon g = new GiantDragon();
        //只能通过方法实现
        GiantDragon g1 = GiantDragon.getInstance();
        GiantDragon g2 = GiantDragon.getInstance();
        GiantDragon g3 = GiantDragon.getInstance();

        System.out.println(g1==g2);
        System.out.println(g2==g3);
    }
}

g1 g2 g3 都指向同一个对象

感觉这个单例模式就是一个私有化构造方法的过程,最多加了一个类属性,帮助外部访问这个对象。

3、懒汉式单例模式

与饿汉式单例模式不同的地方在于--懒汉式单例模式只有在调用getInstance时才实例化对象。

package charactor;

public class GiantDragon {
    private  GiantDragon(){
        //私有化 构造方法 , 使得该类无法在外部通过new进行实例化
    }
        //准备一个私有的类属性,暂时指向null。因为是类属性(静态属性),所以只有一个。
    private static GiantDragon instance ;
    public static GiantDragon getInstance(){
        if (instance == null){
            instance = new GiantDragon();
        }
        return instance;
    }
}


package charactor;

public class TestGiantDragon {
    public static void main(String[] args) {
        //外部实例化不通过
        //GiantDragon g = new GiantDragon();
        //只能通过方法实现
        GiantDragon g1 = GiantDragon.getInstance();
        GiantDragon g2 = GiantDragon.getInstance();
        GiantDragon g3 = GiantDragon.getInstance();

        System.out.println(g1==g2);
        System.out.println(g2==g3);
    }
}

4、什么时候用什么模式?

饿汉式

是立即加载的模式,无论这个对象是否会被用到,都要加载。
因此,对于构造方法里
有很大运算量的代码,要接入数据库等时。启动时,就会很慢。

懒汉式

是一个延时加载的模式。只有在调用对象时才会 实例化对象。
因此,懒汉式比饿汉式略快。但是在第一次调用时,稍慢。

总结

根据业务需求不同来选择。如果有较多的启动和初始化时间,就选用饿汉式,否则就懒汉式。

5、单例模式三元素

面试时经常会问:什么是单例模式?

答:

(1)构造方法私有化;
(2)类属性(静态属性)指向实例;(懒汉式先指向null)
(3)public static 的 getInstance 返回该实例;(懒汉式在该方法中实例化对象)

6、练习

使用饿汉式单例模式, 把Hero类改造成为单例模式

 private Hero(){
        this.name = "小瘪三";//构造方法中初始化
    }

    private static Hero instance = new Hero();

    public static Hero getInstance(){
        return instance;
    }


使用懒汉式单例模式,把Weapon类改造成为单例模式

    private Weapon(){}
    private static Weapon instance;
    
    public static Weapon getInstance(){
        if (null == instance){
            instance = new Weapon();
        }
        return instance;
    }

十三、枚举类型

1、预先定义的常量

枚举enum时一个特殊的类(还是类),使用枚举可以很方便的定义常量
比如设计一个枚举类 季节(Season),里面有四个常量。

package Test;

public class MeiJu {
    public enum Season{//enum是类,Season是枚举类型
        SPRING,SUMMER,AUTUMN,WINTER
        //上述是该类型里的四个常量
    }

    public static void main(String[] args) {
        Season season = Season.SUMMER;
        switch (season){
            case SPRING :
                System.out.println("春天");
                break;
            case SUMMER:
                System.out.println("夏天");
                break;
            case AUTUMN:
                System.out.println("秋天");
                break;
            case WINTER:
                System.out.println("冬天");
                break;
        }
    }
}

主函数里

Season season = Season.SUMMER;

根据选用的常量不同,输出结果不同。

2、使用枚举的好处

如果使用switch的时候,使用int而不是枚举,就可能出现其他结果。

例如

public class HelloWorld {
    public static void main(String[] args) {
        int season = 5;
        switch (season) {
        case 1:
            System.out.println("春天");
            break;
        case 2:
            System.out.println("夏天");
            break;
        case 3:
            System.out.println("秋天");
            break;
        case 4:
            System.out.println("冬天");
            break;
        }
    }
}

 此时就没有结果,事实上也没有第五季。而使用枚举就可以把常量的范围死死地锁定在四个季节里。

3、遍历枚举

借助

增强型for循环,可以很方便的遍历一个枚举类型里有什么常量

  for (Season s : Season.values()){
            System.out.println(s);

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值