A013_static_内部类_final_代码块_枚举

1 内容介绍

1. static关键字(掌握场景)
2. 内部类(掌握)
3. final修饰符(掌握场景)
4. 代码块(了解)
5. 枚举(掌握)

2.static修饰符

2.1 static概念: 关键字、修饰符,表示静态的
static是修饰符:可以修饰的内容: 

目前java代码中存在成员

1.可以修饰普通方法
2.可以修饰字段[ 成员变量 ]
3.可以修饰内部类[暂时不了解]

4.不可以修饰外部类
5.不可以修饰局部变量;
6.不可以修饰构造方法

2.2 修饰成员变量和方法

访问方式: 类名.类中成员(变量、方法)

2.2.1 static修饰后特点
static类级别的修饰符(被修饰的成员属于类)
当用到一个类的时候(new 类名(…)),会将该类的字节码文件加载到元空间,在加载的时候,会将有static修饰的成员,优先加载到静态区。类加载的过程只会执行一次,只有一份。执行完毕后该static修饰的成员会被所有以该字节码文件为模板创建的对象所共享。

举例说明:
设计一个学生类,其中有name-姓名 字段,是否应该加static 呢?
效果:加了static修饰的字段:该字段被该类所有对象共享:当一个对象修改了该字段,其他对象使用该字段,都是修改之后的值

/**
 *	学生类
 */
public class Student {
	/**用static修饰name,会被所有用Student模板创建的对象共享*/
	static String name;
	public Student(String n) {
		name = n;//将传入的n赋值给成员变量name
	}
	public static void testStatic() {
		System.out.println("static修饰方法");
	}
	/**
	 * 内部类:今天只需要了解
	 */
	static class Inner{
	}
}

/**
 *	static测试类
 */
public class StudentTest {

	public static void main(String[] args) {
		//调用有参构造,创建对象并且直接赋值
		Student stu1 = new Student("隔壁老王");
		//打印stu1的名字
		System.out.println(stu1.name);//隔壁老王
		
		//调用有参构造,创建对象并且直接赋值
		Student stu2 = new Student("隔壁老孙");
		//打印stu2的名字
		System.out.println(stu2.name);//隔壁老孙
		//重新打印stu1的名字
		System.out.println(stu1.name);//隔壁老孙(居然不是隔壁老王)
	}
}
加static修饰字段前后对比的堆栈费分析

加static之前
在这里插入图片描述
加static之后
在这里插入图片描述

2.3 static作用
  1. 可以避免对象被多次创建。例如,后面学习的:单例模式
  2. 需要几个对象有共享的数据。
  3. 一般情况下,修饰成员变量的时候,是配合 public static final 一起使用,被修饰的叫做:全局常量一般 用全大写+下划线的命名方式,例如: MAX_VALUE
  4. 修饰方法,只是为了方便调用。 类名.方法名(…)。例如: Arrays中全部都是工具方法(static)Arrays.toString(…);
  5. 最近面向对象语法,我们一般写的方法都写成非静态,除非非得使用static
2.4 static 小结
最近都不要使用static修饰变量
2.5 变量重新分类

成员变量(也叫字段) : 静态的(类变量)和非静态的(实例变量instance)
static int age; 类变量, 有static修饰的成员变量(字段);
int age; 实例变量,没有static修饰的成员变量(字段);
分类详解:

           位置          是否有static    生命周期(开始)        生命周期(结束)
类变量	   类中 		  √             类被加载的时候          类被卸载的时候
实例变量	   类中                         创建对象的时候          对象失去外部引用
局部变量	   方法内部(形参,代码块)	    方法被调用的时候        方法调用完毕

3 内部类和匿名内部类

3.1 内部类

什么是内部类,把一个类定义在另一个类的内部,把里面的类称之为内部类,把外面的类称之为外部
类。(能认识内部类即可)
在这里插入图片描述
内部类可以看作和字段、方法一样,是外部类的成员,而成员可以有 static 修饰。

  1. 静态内部类:使用 static 修饰的内部类,那么访问内部类直接使用外部类名来访问
  2. 实例内部类:没有使用 static 修饰的内部类,访问内部类使用外部类的对象来访问
  3.  局部内部类:定义在方法中的内部类,一般不用
  4.  匿名内部类:特殊的局部内部类,适合于仅使用一次使用的类

对于每个内部类来说,Java 编译器会生成独立.class 文件。

  1. 静态和实例内部类:外部类名$内部类名字
  2. 局部内部类:外部类名$数字内部类名称
  3. 匿名内部类:外部类名$数字
3.2 匿名内部类(掌握)

在多态 USB 的案例中,当新增一种 USB 规范的设备,此时需要单独使用一个文件来定义一个新的类。
比如,新增一个 USB 规范的打印机设备。

public class Print implements IUSB{ 
	public void swapData() { 
		System.out.println("打印...."); 
	} 
} 

把打印机安装在主板上。

public class USBDemo { 
	public static void main(String[] args) { 
		// 创建主板对象 
		MotherBoard board = new MotherBoard(); 
		// 创建打印机对象 
		Print p = new Print(); 
		//把打印机安装在主板上 
		board.plugin(p); 
	} 
}

如果这一个 Print 类只需要使用一次的话,就完全没有必要单独定义一个 Java 文件,直接使用匿名内
部类来完成。
匿名内部类,可以使用父类构造器和接口名来完成。
针对类,定义匿名内部类来继承父类(使用较少):

new 父类构造器([实参列表]){ 
//匿名内部类的类体部分 
} 

针对接口,定义匿名内部类来实现接口(使用较多):

new 接口名称(){ 
//匿名内部类的类体部分 
} 

注意:这里不是根据接口创建对象,而是一种语法而已。

board.plugin(new IUSB() { 
	public void swapData() { 
	System.out.println("打印...打印..."); 
	} 
	}); 

其实匿名内部类,底层依然还是创建了一份字节码文件 USBDemo$1,其反编译代码为:
在这里插入图片描述

4 final修饰符

4.1 final概念

关键字,修饰符,表示最终的。就是一旦修饰一个成员,该成员就不能再被修改了。

4.2 final作用:
可以修饰:
		外部类:太监类,不能被继承
		实例变量:必须在声明的时候赋值或者在构造方法中赋值
		类变量: 必须在声明的时候赋值 
		实例方法:不能被子类重写				
		类方法:不能被子类重写
		内部类:
		局部变量:
		
不能修饰:
		构造方法
代码演示:
public class FinalTest {
   /**实例变量	在堆中*/
   private final int A;
       private final int B = 1;//声明的时候直接赋值
   
   /**类变量	在静态区*/
   static final int C = 1;//声明的时候直接赋值
   
   /**
    * 构造方法,final不能修饰
    */
   public FinalTest() {
   	A = 1;//在构造方法中赋值
   }
   
   /**
    * 实例方法,不能被重写
    */
   public final void test() {
   	
   }
   
   /**
    * 类方法,不能被重写
    */
   public static final void testStatic() {
   	
   }
   
   /**
    * 内部类,不能被继承
    */
   final class Inner{
   	
   }
   
   public static void main(String[] args) {
   	final int a;//在栈帧中
   	a = 2;//一旦被final修饰就不能再修改
   	System.out.println(a);
   	
//		a = 3;//一旦被final修饰就不能再修改
   }
}

5 代码块

3.1 什么是代码块

简单的认为就是一对 { };
看到{} 应该想到作用域问题;

3.2 代码块分类
静态代码块
		语法:直接声明在类中,前面有static修饰
		static {
//一般用来初始化数据,在类加载的时候就初始化完成
}				
① {}直接写在类中的,有static修饰;
② 在类加载的时候执行和创建对象没有关系,且只会执行一次
③ 优先于主方法的执行的(加载完之后,JVM才找主方法执行)
④ 可以在类加载的时候做一些初始化的操作(例如JDBC的驱动的加载)
局部代码块(普通代码块)
基本没用,在方法中的一对{},一般不会单独使用,一般是配合if或者循环等语句使用
构造代码块
语法:直接声明在类中,前面没有static修饰
	{
		//一般没用
		该代码块中代码,会生成在构造方法内部,super()下面,其他代码上面
 	}
② 直接写在类中的代码块(没有static修饰);
③ 编译完毕之后会在每一个构造方法里面复制一份;
④ 创建一个对象(调用一次构造方法)就会执行一次
⑤ 可以给对象的实例字段初始化值(基本不用)
3.3 掌握继承关系的类中各个成员(包括类变量)的执行顺序

有继承关系的执行流程:
从最高父类的静态代码块开始加载,然后逐级向下加载,直到加载到当前类的静态代码块结束。
再从最高父类的构造代码块开始执行,然后是构造方法,然后逐级向下执行,直到执行到当前类的构造代 码块、构造方法结束。

public class CodeBlock {
	/**
	 * 静态代码块:只会执行一次
	 */
	static {
		//一般用来初始化数据,在类加载的时候就初始化完成
		System.out.println("静态代码块");
	}
	
	/**
	 * 构造代码块:(基本没用)每次创建对象都会执行
	 */
	{
		//该代码块中代码,会在生成在构造方法内部,super()下面,其他代码上面
		System.out.println("构造代码块");
	}
	
	/**
	 * 构造方法
	 */
	public CodeBlock() {
		super();
		//System.out.println("构造代码块");
		System.out.println("构造方法!");
	}
	
	public static void main(String[] args) {
		new CodeBlock();
		new CodeBlock();
		
		{//普通代码块
			int a = 1;
		}
//		System.out.println(a);//出了作用域范围了
	}

}

6 枚举

6.1枚举的引入

需求: 设计一个类专门用了表示性别(性别的取值比较固定:男、女、未知)

/**
 *	设计一个类,表示性别
 *		3种:男MAN、女WOMEN、未知UNKNOWN
 *
 *	侵入性问题:一个类中包含其他的数据类型都是有侵入性。尽可能避免。
 */
public class Gender {//表示性别类
//	public static final String MAN = "男";//MAN是String的一个对象
	//在Gender类中使用String类型,有侵入性问题。那么,能不能用Gender自己类型的对象表示一个男? 可以
	public static final Gender MAN = new Gender();//MAN是Gender的一个对象
	
//	public static final String WOMEN = "女";//WOMEN是String的一个对象
	public static final Gender WOMEN = new Gender();//WOMEN是Gender的一个对象
	
//	public static final String UNKNOWN = "未知";//UNKNOWN是String的一个对象
	public static final Gender UNKNOWN = new Gender();//UNKNOWN是Gender的一个对象
	
	@Override
	public String toString() {
		/*
		 * 希望打印格式如下:
		 * 	是MAN打印男
		 * 	是WOMEN打印女
		 * 	是UNKNOWN打印未知
		 */
		if (this == MAN) {
			return "男";
		}else if (this == WOMEN) {
			return "女";
		}else{
			return "未知";
		}
	}
}

测试类:
/**
 *	测试性别,枚举引入
 */
public class GenderTest {
	public static void main(String[] args) {
		//调用全局常量方式,类名.常量名
		System.out.println(Gender.MAN);//男
		System.out.println(Gender.WOMEN);//女
		System.out.println(Gender.UNKNOWN);//未知
	}

}

是不是感觉上面的代码方式很麻烦 ?那么,有没有更简单的方法呢? 用枚举,那么是什么是枚举?

6.2 枚举概述
6.2.1 什么是枚举

枚举是JDK1.5引入的一种和类非常类似的新结构;
枚举的出现解决了哪些问题?
枚举类解决了一些取值比较固定的场景,简化了类中的常量字段。

6.2.2 枚举的作用和使用场景

作用:简化类中的常量声明这种代码,是代码变得更加优雅
使用场景:vip、生肖、段位、QQ状态、血型、性别、星座、月份、礼拜…

6.2.3 枚举的基本语法
① 声明语法:
public enum 枚举类名字{
	字段
	实例变量
	实例方法
	类方法
	构造方法 - 枚举中的构造方法默认都是private修饰,不能够是publicprotected修饰
		构造方法作用:只能在当前枚举类中使用,就是给当前枚举类对象初始化实例变量的
}
语法:
public enum Gender {//表示性别类
//	MAN,//相当于一个常量public static final Gender = new Gender();
	MAN(),//MAN这里也可以这样写,这里就是在调用当前枚举类的无参构造
	WOMEN("女"),//这里就是在调用当前枚举类的String类型有参构造
	UNKNOWN,
	其他;//也可以写中文,但是不建议,建议写法:WOMEN("");
	private String name;//实例变量,属于每一个枚举对象,如:MAN,WOMEN..
	
	/**
	 * 无参构造
	 */
	Gender() {//默认有一个隐式的private修饰
		
	}
	
	/**
	 * 有参构造
	 */
	Gender(String name) {//默认有一个隐式的private修饰
		this.name = name;
	}
	
	@Override//实例方法
	public String toString() {
		if (this == MAN) {
			return "男";
		}else if (this == WOMEN) {
			return "女";
		}else{
			return "未知";
		}
	}
	
	/**
	 * 类方法
	 */
	public static void test() {
	
	}
}

② 枚举类编译完毕也同样生成字节码文件
③ 每一个自定义的枚举类型都(隐式的)继承于 Enum抽象类,因此我们的枚举对象可以调用到Enum中的方法的(看API )。但是不能显示的写出继承关系。

//上面性别代码用枚举简化
public enum Gender {//表示性别类
	MAN,//MAN字段等同于原来的public static final Gender MAN = new Gender();这一句
	WOMEN,
	UNKNOWN;
	@Override
	public String toString() {//该方法是从Enum中间接继承于Object
		if (this == MAN) {
			return "男";
		}else if (this == WOMEN) {
			return "女";
		}else{
			return "未知";
		}
	}
}
测试代码:
/**
 *	测试枚举
 */
public class GenderTest {

	public static void main(String[] args) {
		//调用全局常量方式,类名.常量名
		System.out.println(Gender.MAN);//男
		System.out.println(Gender.WOMEN);//女
		System.out.println(Gender.UNKNOWN);//未知
	}
}
6.4 枚举注意事项
  1. 字段之间用逗号,最后一个字段用分号结束
  2. 可以写中文字段,但是不建议
  3. 枚举类都会隐式继承了Enum类(所有枚举类的基类),不能显示写出来
  4. 构造方法必须是private的
6.5 枚举常用的两种写法
方式1代码:
public enum Gender {
	MAN,
	WOMEN,
	UNKNOWN;
}

方式2代码:
public enum Gender {

	MAN("男"),
	WOMEN("女");

	private String name;
	
	private Gender(String name) {
		this.name = name;
	}
	
	@Override
	public String toString() {
		return this.name;
	}
}


配合switch语句练习:
/**
 * 测试枚举
 */
public class GenderTest {

	public static void main(String[] args) {
		// 调用全局常量方式,类名.常量名
		Gender gender = Gender.MAN;

		switch (gender) {
			case MAN:
				System.out.println("男左");
			break;
			case WOMEN:
				System.out.println("女优");
			break;
			default:
				System.out.println("未知");
			break;
		}
	}

}

7 课程总结

7.1.重点

枚举,抽象类,接口

7.2.难点

接口和抽象类的使用场景、匿名内部类

8 作业

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值