当一个属性被static修饰的时候,就叫做类属性,又叫做静态属性
- 当一个属性被声明成类属性,那么所有的对象,都共享一个值
- 与对象属性对比:
- 不同对象的 对象属性 的值都可能不一样。
- 比如盖伦的hp 和 提莫的hp 是不一样的。
- 但是所有对象的类属性的值,都是一样的
- 类属性
- 类属性: 又叫做静态属性
- 对象属性: 又叫实例属性,非静态属性
- 如果一个属性声明成类属性,那么所有的对象,都共享这么一个值
- 给英雄设置一个类属性叫做“版权" (copyright), 无论有多少个具体的英雄,所有的英雄的版权都属于 Riot Games公司。
package charactor; public class Hero { public String name; //实例属性,对象属性,非静态属性 protected float hp; static String copyright;//类属性,静态属性 public static void main(String[] args) { Hero garen = new Hero(); garen.name = "盖伦"; Hero.copyright = "版权由Riot Games公司所有"; System.out.println(garen.name); System.out.println(garen.copyright); Hero teemo = new Hero(); teemo.name = "提莫"; System.out.println(teemo.name); System.out.println(teemo.copyright); } }
- 访问类属性
- 访问类属性有两种方式
- 1. 对象.类属性
teemo.copyright
- 2. 类.类属性
Hero.copyright
- 这两种方式都可以访问类属性,访问即修改和获取,但是建议使用第二种 类.类属性 的方式进行,这样更符合语义上的理解
- 什么时候使用对象属性,什么时候使用类属性
- 如果一个属性,每个英雄都不一样,比如name,这样的属性就应该设计为对象属性,因为它是跟着对象走的,每个对象的name都是不同的
- 如果一个属性,所有的英雄都共享,都是一样的,那么就应该设计为类属性。比如血量上限,所有的英雄的血量上限都是 9999,不会因为英雄不同,而取不同的值。 这样的属性,就适合设计为类属性
- 类方法: 又叫做静态方法
- 对象方法: 又叫实例方法,非静态方法
- 访问一个对象方法,必须建立在有一个对象的前提的基础上
- 访问类方法,不需要对象的存在,直接就访问
- 类方法
package charactor; public class Hero { public String name; protected float hp; //实例方法,对象方法,非静态方法 //必须有对象才能够调用 public void die(){ hp = 0; } //类方法,静态方法 //通过类就可以直接调用 public static void battleWin(){ System.out.println("battle win"); } public static void main(String[] args) { Hero garen = new Hero(); garen.name = "盖伦"; //必须有一个对象才能调用 garen.die(); Hero teemo = new Hero(); teemo.name = "提莫"; //无需对象,直接通过类调用 Hero.battleWin(); } }
- 调用类方法
- 调用类方法也有两种方式
- 1. 对象.类方法
garen.battleWin();
- 2. 类.类方法
Hero.battleWin();
- 这两种方式都可以调用类方法,但是建议使用第二种 类.类方法 的方式进行,这样更符合语义上的理解。
- 并且在很多时候,并没有实例,比如在前面练习的时候用到的随机数的获取办法
- Math.random()
- random()就是一个类方法,直接通过类Math进行调用,并没有一个Math的实例存在。
- 什么时候设计对象方法,什么时候设计类方法
- 如果在某一个方法里,调用了对象属性,比如
public String getName(){ return name; }
- name属性是对象属性,只有存在一个具体对象的时候,name才有意义。
- 如果方法里访问了对象属性,那么这个方法,就必须设计为对象方法
- 如果一个方法,没有调用任何对象属性,那么就可以考虑设计为类方法,比如
public static void printGameDuration(){ System.out.println("已经玩了10分50秒"); }
- printGameDuration 打印当前玩了多长时间了,不和某一个具体的英雄关联起来,所有的英雄都是一样的。 这样的方法,更带有功能性色彩
- 就像取随机数一样,random()是一个功能用途的方法
Math.random()
- 对象属性初始化
- 对象属性初始化有3种
- 1. 声明该属性的时候初始化
- 2. 构造方法中初始化
- 3. 初始化块
package charactor; public class Hero { public String name = "some hero"; //声明该属性的时候初始化 protected float hp; float maxHP; { maxHP = 200; //初始化块 } public Hero(){ hp = 100; //构造方法中初始化 } }
- 类属性初始化
- 类属性初始化有2种
- 1. 声明该属性的时候初始化
- 2. 静态初始化块
package charactor; public class Hero { public String name; protected float hp; float maxHP; //物品栏的容量 public static int itemCapacity=8; //声明的时候 初始化 static{ itemCapacity = 6;//静态初始化块 初始化 } public Hero(){ } public static void main(String[] args) { System.out.println(Hero.itemCapacity); } }
- 对象属性的初始化有三种方式
- 故意把初始化块,放在构造方法下面,问题:
- 这三种方式,谁先执行?谁后执行?
- 设计一个类方法
public static String getName(String pos){ }
- 在属性初始化的时候,调用这个方法的返回值。
- 观察这个方法的输出顺序
package charactor; public class Hero { public String name =Hero.getName("属性声明") ; public Hero(){ name = Hero.getName("构造方法"); } { name = Hero.getName("初始化块"); } public static String getName(String pos){ String result= "name "+pos; System.out.println(result); return result; } public static void main(String[] args) { new Hero(); } }
- 输出结果:
- LOL里有一个怪叫大龙GiantDragon,只有一只,所以该类,只能被实例化一次
- 单例模式
- 单例模式又叫做 Singleton模式,指的是一个类,在一个JVM里,只有一个实例存在。
- 饿汉式单例模式
- GiantDragon 应该只有一只,通过私有化其构造方法,使得外部无法通过new 得到新的实例。
- GiantDragon 提供了一个public static的getInstance方法,外部调用者通过该方法获取12行定义的对象,而且每一次都是获取同一个对象。 从而达到单例的目的。
- 这种单例模式又叫做饿汉式单例模式,无论如何都会创建一个实例
package charactor; public class GiantDragon { //私有化构造方法使得该类无法在外部通过new 进行实例化 private GiantDragon(){ } //准备一个类属性,指向一个实例化对象。 因为是类属性,所以只有一个 private static GiantDragon instance = new GiantDragon(); //public static 方法,提供给调用者获取12行定义的对象 public static GiantDragon getInstance(){ return instance; } }
- 调用:
package charactor; public class TestGiantDragon { public static void main(String[] args) { //通过new实例化会报错 // GiantDragon g = new GiantDragon(); //只能通过getInstance得到对象 GiantDragon g1 = GiantDragon.getInstance(); GiantDragon g2 = GiantDragon.getInstance(); GiantDragon g3 = GiantDragon.getInstance(); //都是同一个对象 System.out.println(g1==g2); System.out.println(g1==g3); } }
- 懒汉式单例模式
- 懒汉式单例模式与饿汉式单例模式不同,只有在调用getInstance的时候,才会创建实例
package charactor; public class GiantDragon { //私有化构造方法使得该类无法在外部通过new 进行实例化 private GiantDragon(){ } //准备一个类属性,用于指向一个实例化对象,但是暂时指向null private static GiantDragon instance; //public static 方法,返回实例对象 public static GiantDragon getInstance(){ //第一次访问的时候,发现instance没有指向任何对象,这时实例化一个对象 if(null==instance){ instance = new GiantDragon(); } //返回 instance指向的对象 return instance; } }
- 调用:
package charactor; public class TestGiantDragon { public static void main(String[] args) { //通过new实例化会报错 // GiantDragon g = new GiantDragon(); //只能通过getInstance得到对象 GiantDragon g1 = GiantDragon.getInstance(); GiantDragon g2 = GiantDragon.getInstance(); GiantDragon g3 = GiantDragon.getInstance(); //都是同一个对象 System.out.println(g1==g2); System.out.println(g1==g3); } }
- 什么时候使用饿汉式,什么时候使用懒汉式
- 饿汉式是立即加载的方式,无论是否会用到这个对象,都会加载。
- 如果在构造方法里写了性能消耗较大,占时较久的代码,比如建立与数据库的连接,那么就会在启动的时候感觉稍微有些卡顿。
- 懒汉式,是延迟加载的方式,只有使用的时候才会加载。 并且有线程安全的考量(鉴于同学们学习的进度,暂时不对线程的章节做展开)。
- 使用懒汉式,在启动的时候,会感觉到比饿汉式略快,因为并没有做对象的实例化。 但是在第一次调用的时候,会进行实例化操作,感觉上就略慢。
- 看业务需求,如果业务上允许有比较充分的启动和初始化时间,就使用饿汉式,否则就使用懒汉式
- 单例模式三元素
- 什么是单例模式?
- 1.构造方法私有化
- 2. 静态属性指向实例
- 3. public static的 getInstance方法,返回第二步的静态属性
- 案例演示:
- 使用饿汉式单例模式, 把Hero类改造成为单例模式
- 使用懒汉式单例模式,把Item类改造成为单例模式
package charactor; public class Hero { private Hero() { } private static Hero instance = new Hero(); public static Hero getInstance() { return instance; } }
- 预先定义的常量
- 枚举enum是一种特殊的类(还是类),使用枚举可以很方便的定义常量
- 比如设计一个枚举类型 季节,里面有4种常量
public enum Season { SPRING,SUMMER,AUTUMN,WINTER }
- 一个常用的场合就是switch语句中,使用枚举来进行判断
- 注:因为是常量,所以一般都是全大写
public class HelloWorld { public static void main(String[] args) { Season season = Season.SPRING; 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; } } }
- 使用枚举的好处
- 假设在使用switch的时候,不是使用枚举,而是使用int,而int的取值范围就不只是1-4,有可能取一个超出1-4之间的值,这样判断结果就似是而非了。(因为只有4个季节)
- 但是使用枚举,就能把范围死死的限定在这四个当中
- 遍历枚举
- 借助增强型for循环,可以很方便的遍历一个枚举都有哪些常量
public class HelloWorld { public static void main(String[] args) { for (Season s : Season.values()) { System.out.println(s); } } }
- 案例演示:
英雄联盟中有这么一些分类 TANK (坦克) WIZARD (法师 ) ASSASSIN (刺客) ASSIST (辅助) WARRIOR (近战) RANGED (远程 ) PUSH (推进) FARMING (打野) 设计一个枚举类型HeroType,使用上述分类作为常量 再编写一段switch语句,把每种枚举常量输出为中文字符串
package charactor; public enum HeroType { TANK, WIZARD, ASSASSIN, ASSIST, WARRIOR, RANGED, PUSH, FARMING }
- 调用:
package charactor; public class Hero { public String name; // 姓名 public static void main(String[] args) { HeroType type = HeroType.ASSASSIN; switch (type) { case TANK: System.out.println("坦克"); break; case WIZARD: System.out.println("法师"); break; case ASSASSIN: System.out.println("刺客"); break; case ASSIST: System.out.println("辅助"); break; case WARRIOR: System.out.println("近战"); break; case RANGED: System.out.println("远程战"); break; case PUSH: System.out.println("推进"); break; case FARMING: System.out.println("打野"); break; } } }