类属性11.13
当一个属性被static修饰的时候,就叫做类属性,有叫静态属性
当一个属性被说明成类属性,那么所有的对象,都共享一个值
与对象属性的对比:不同对象的 对象属性 的值都是可能不一样的,但是所有对象的类属性的值,都是一样的
类属性:有叫静态属性
对象属性:又叫实例属性,非静态属性
如果一个属性声明成类属性,那么所有的对象,都共享这个值
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);
}
}
访问类属性
访问类属性有2种方式:
- 对象-类属性 teemo.copyright
- 类-类属性 Hero.copyright
2种方式都可以访问类属性,访问即修改和获取
建议使用第二种类-类属性的方式进行
使用情况
什么情况下合理选择对象属性或类属性:
- 如上代码,每个英雄都不一样,比如name,这样的属性设计为对象属性,因为是跟着对象走,每个对象的name都是不一样
- 如果一个属性,所有的英雄都是共享,都是一样,就应该设计为类属性。比如血量上限max
类方法11.20
类方法:静态方法
对象方法:实例方法,非静态方法
说明:访问一个对象方法,必须建立在有一个对象的前提的基础上访问类方法,不需要对象的存在,直接就访问
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();
}
}
访问类方法
同访问类属性一样,调用类方法也有2种方式
- 对象 类方法 garen.battleWin();
- 类 类方法 Hero.battleWin();
建议使用第2种方式 类 类方法较为符合语义的理解
在很多时候,没有实例,比如前面练习的随机数的获取方法Math.random()
random()就是类方法,直接通过类Math进行调用,没有Math实例
对象方法 类方法 的选用(设计)
//情况一
public String getNmae(){
return name;
}
/*name属性是对象属性,只有存在一个具体对象的时候,name才会有意义
如果方法访问了对象属性,那么这个方法,就必须要设计为对象属性
*/
//情况二
public static void printGameDuration(){
System.out.prinrln("已经玩了10分30秒");
}
/*printGameDuration 打印当前玩了多长时间,不和某一个具体的关联起来,所有的英雄都是一样的。
这样的方法,更带有功能性色彩就像取随机数一样,random()是一个功能用途的方法
*/
练习:类方法
Q:在一个类方法中,直接调用一个对象方法,比如在battleWin中调用die()能否直接调用?原因是什么?
A:不能调用,因为静态方法优先对象方法加载进内存,无法加载对象方法。静态方法,静态属性是跟着类加载,此时没有创建对象,所以静态方法不能调用非静态方法。但反过来对象方法是随着对象的出生而出生的,所以它比静态方法出生的要晚,因此对象方法中科院调用静态方法。类方法不需要实例化,对象方法需要实例化一个对象才能调用。
属性初始化
有3种对象属性初始化
- 声明该属性的时候初始化
- 构造方法中初始化
- 初始化块
package charactor;
public class Hero{
public String name = "some hero";//声明该属性的时候初始化
protected float hp;
float maxHP;
{
maxHP = 200;//初始化块
}
public Hero(){
hp = 100;//构造方法中初始化
}
}
类属性初始化
类属性初始化有2种
- 声明该属性的时候初始化
- 静态初始化块
package charactor;
public class Hero{
public String name;
protected float hp;
float maxHP;
//物品栏的容量
public static int itemCapacity = 6;//声明的时候 初始化
static{
itemCapacity = 5;//静态初始化 初始化
}
public Hero(){
}
public static void main(Stirng[] args){
System.out.println(Hero.itemCapacity);
}
}
练习:对象属性的初始化有3种方式,谁先执行?谁最后执行?
package charactor;
public class Hero{
public String name = "some hero";
public Hero(){
name = "one hero";
}
{
name = "the hero";
}
}
/*先执行:声明该属性的时候初始化
再执行:初始化块
最后执行:构造方法中初始化
*/
单例模式
说明:Singleton模式,指的是一个类,在一个JVM里,只有一个实例存在
恶汉式单例模式
GiantDragon 应该只有一只,通过私有化其构造方法,使得外部无法通过new得到新的实例。
GianDragon 提供一个public static的getlnstance方法,外部调用者通过该方法获取12行定义的对象,而且每一次都是获取同一个对象。这种单例模式又叫恶汉式单例模式,无论如何都会创建一个实例。
//GiantDragon.java
package charactor;
public class GiantDragon{
private GiantDragon(){ //私有化构造方法使得该类无法在外部通过new进行实例化
}
//准备一个类属性,指向一个实例化对象,因是类属性,所以只有一个
private static GiantDragon instance = new GiantDragon();
//public static 方法,提供给调用者获取12行定义的对象
public static GiantDragon getInstance(){ //Instance单例使用
return instance;
}
}
//TestGiantDragon.java
pakage charactor;
public class TestGiantDragon{
public static void main(String[] args){
//通过new实例化会报错
//GiantDragon g = new GiantDragon();
//只能通过getInstance得到对象 java中getInstance就代表单例模式
GiantDragon g1 = GiantDragon.getInstance();
GiantDragon g2 = GiantDragon.getInstance();
GiantDragon g3 = GiantDragon.getInstance();
System.out.println(g1==g2);
System.out.println(g1==g3);
//打印后发现都是同一个对象
}
}
懒汉式单例模式
懒汉式单例模式与恶汉式单例模式不同,只有在调用getInstance的时候,才会创建实例
//GiantDragon.java
package charactor;
public class GiantDragon{
//私有化构造方法使得该类无法在外部通过new进行实例化
private GiantDragon(){
}
//准备一个类属性,用于指向一个实例化对象,但是暂时指向nu11
private static GiantDragon instance;
//public staic 方法,返回实例对象
public static GiantDragon getInstance(){
//第一次访问的时候,发现instance没有指向对象,这时实例化一个对象
if(nu11==instance){
instance = new GiantDragon();
}
返回 instance指向对象
return instance;
}
}
//TestGiantDragon.java
packge charactor;
public class TestGiantDragon{
public static void mian(Sting[] args){
//new实例化会报错
//GiantDragon g = new GiantDragon();
//只能通过getInstance得到对象
GiantDragon g1 = GiantDragon.getInstance();
GiantDragon g2 = GiantDragon.getInstance();
GiantDragon g3 = GiantDragon.getInstance();
System.out.prinln(g1==g2);
System.out.prinln(g2==g3);
//打印后发现同一个对象
}
}
恶汉式和懒汉式的选择
恶汉式立即加载,无论是否用到这个对象,都会进行加载,所以较慢。
如果在构造方法里写了性能消耗较大,占时较久的代码,比如建立与数据库的连接,那么就会在启动的时候有些卡顿。
懒汉式是延迟加载的方式,只有使用的时候才会加载。有线程安全的考量,使用懒汉式,在启动时会比恶汉式快,因为没有做对象的实例化(即创建对象)。但在第一次调用时,会进行实例化操作,会略慢一点。
看业务需求,如果业务上允许比较充分的启动和初始化时间,就使用恶汉式
单例模式三元素
什么事单例模式?
- 构造方法私有化
- 静态属性指向实例
- public static 的getInstance方法,返回第二步的静态属性
枚举类型
预先定义的常量:枚举enum式一种特殊的类(还是类),使用枚举可以很方便的定义常量,比如设计一个枚举类型 季节,里面有4种常量
public enum Season{
SPRING,SUMMER,AUTUMN,WINTER
}
//一个常用的场合就是switch语句中,使用枚举来进行判断
//注意:常量 一般都是大写
//HelloWorld.java
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;
}
}
}
//Season.java
public enum Season{
SPRING,SUMMER,AUTUMN,WINTER
}
为什么会使用枚举,枚举有哪些好处?
如在使用switch的时候,不是使用的枚举,而是使用int,而int的取值范围不只是1-4,有可能取超出该区间的之,这样就不能判断结果了,一位值没有唯一对应。
如使用枚举,就能吧范围限定在四个当中
SPRING,SUMMER,AUTUMN,WINTER
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;
}
}
}
遍历枚举
借助增强型for循环,可以方便的便利一个枚举都有哪些常量
public class HelloWorld{
public static void main(String[] args){
for (Season s : Season.values()){
System.out.println(s);
}
}
}