一、引用
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中的参数名与属性名不一致,成功赋值。
setName3中this.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代表调用该构造函数的h或h1 相当与 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);