Java基础——代码块&内部类

本文详细介绍了Java中的代码块和内部类。代码块分为静态和非静态,用于初始化类和对象,静态代码块在类加载时执行,非静态代码块在对象创建时执行。内部类作为类的成员,可以是静态或非静态,可以拥有自己的属性和方法,且可以访问外部类的结构。成员内部类和局部内部类在实例化和使用上有所不同,内部类会生成单独的字节码文件。在局部内部类中使用外部方法的局部变量需为final类型。

一、代码块

类的成员之四:代码块(初始化块)

1. 代码块概述

  • 代码块的作用:用来初始化类、对象

  • 代码块如果要修饰的话:只能用static修饰(其他的都不行)

  • 分类:静态代码块 vs 非静态代码块


2. 静态代码块

  • 内部可以有输出语句【就像方法体一样】
  • 随着类的加载而执行
    不仅是像静态方法那样随着类的加载而加载,还执行了;(因为没有名字,没法由我们来调用,因此需要自动执行)】
    【而且由于是静态结构,只加载一次】
    作用:可以初始化类的信息(静态属性等)
  • 一个类中可以定义多个静态代码块,类加载的时候都会执行到,执行顺序按照声明时的上下顺序依次执行
  • 静态代码块的执行,优先于非静态代码块的执行【很显然,因为静态结构的加载早于非静态结构】

3. 非静态代码块:

  • 内部可以有输出语句
    随着对象的创建而执行
    【每创建一个对象,就会执行一次】
    【作用:可以在创建对象时,对对象的属性等进行初始化】
  • 一个类中也可以定义多个非静态代码块,对象创建的时候都会执行到,执行顺序按照声明时的上下顺序依次执行

4. 注意点

  • 虽然可以定义多个代码块,但没必要,因为代码块只能是加载类/创建对象时,统一调用,所以干脆把多个合成一个就完事了
  • 代码块的作用:就是对类、对象进行初始化,因此使用频率不高,但也有用处(后面数据库连接池就用到了)

【代码块的执行,先于构造器!!!】
【类的加载,先于main()方法!!!】
(很显然,main()方法虽然是程序的入口,但本身也是一个静态方法,必须是通过类调用,只不过是虚拟机来通过类调用的,而非我们自己;既然是通过类调用的,显然必须先加载类)



二、内部类

类的内部成员之五:内部类

1. 内部类概述

【有些时候,在一个类当中,某个成分作为属性出现难以表明其细节,在类外单独定义一个类又没必要(因为只有本类使用),这种时候就适合把这个成分单独定义成一个结构,即"内部类"(本身是一个类,能定义属性、方法、构造器,同时又是外部类的一个属性)】

  • Java中允许将一个类A声明在另一个类B中,则类A就是"内部类",类B则称为"外部类"
  • 内部类的分类:成员内部类(静态、非静态) vs 局部内部类(方法内、代码块内、构造器内)
    【像变量一样,分为成员变量和局部变量】

2. 成员内部类

2.1 一方面,作为外部类的成员:

  • 可以调用外部类的结构
  • 可以被static修饰
  • 可以使用全部的4种权限修饰符
    【正常一个类不能用static修饰,只能使用两种权限修饰符】

2.2 另一方面,作为一个类:

  • 类内可以定义属性、方法、构造器等
  • 可以被final修饰,表示此类不能被继承(言外之意,不用final修饰,此类就可以被继承)
  • 可以被abstract修饰

3. 内部类主要只需关注如下3个问题:

4.1 如何实例化成员内部类的对象

//创建静态的成员内部类的实例
Person.Dog dog = new Person.Dog();

//创建非静态的成员内部类的实例
Person p = new Person();
Person.Bird bird = p.new Bird(); //(非静态结构,都要通过对象来调用)

4.2 如何在成员内部类中区分调用外部类的结构

【尤其是重名的时候】

public void display(String name){
	System.out.println(name); //方法的形参
	System.out.println(this.name); //内部类Bird的属性
	System.out.println(Person.this.name); //外部类Person的属性
	getComparable(); //对于外部类中不重名结构,可以直接调用,前面省Person.this【注意:不是this,此时this指代的是内部类本身,而非外部类】
}

4.3 开发中局部内部类的使用

//局部内部类的常见用法:希望调用方法的时候,完成一个实现了某个接口的"实现类",同时帮我造个该实现类的对象,并返回给我
//例如:返回一个实现了Comparable接口的类的对象
public Comparable getComparable(){
	//创建一个实现了Comparable接口的类:局部内部类【为什么不把这个方法放在外面?因为外面用不到】
	//方式一:
	class MyComparable implements Comparable{
		@Override
		public int compareTo(Object o) {
			// TODO Auto-generated method stub
			return 0;
		}
	}	
	return new MyComparable();
}

【内部类:同时满足 类 & 类的成员 的一切要求,(前面讲过的内容全部适用,比如:静态内部类中,不能调用外部类的非静态结构)】

代码实例:

//测试
public class InnerClassTest {
	public static void main(String[] args) {
		//1. 关注问题1:如何实例化成员内部类的对象
		//创建Dog实例(静态的成员内部类)
		Person.Dog dog = new Person.Dog();
		
		//创建Bird实例(非静态的成员内部类)
		Person p = new Person();
		Person.Bird bird = p.new Bird(); //(非静态结构,都要通过对象来调用)
		bird.display("黄鹂");
	}
}

//这里只是举例来测试内部类的使用规则,(实际上Dog、Bird在Person里显然不合理)
class Person{
	String name = "小明";
	int age;
	
	//静态内部类
	static class Dog{
		public void print(){
			System.out.println("我是一只汪汪汪");
		}
	}
	
	//非静态成员内部类
	class Bird{
		String name = "喜鹊";
		
		public void sing(){
			System.out.println("我是一只小小鸟");
		}
		
		public void display(String name){
			//2. 关注问题2:如何在成员内部类中区分调用外部类的结构
			System.out.println(name); //方法的形参
			System.out.println(this.name); //内部类Bird的属性
			System.out.println(Person.this.name); //外部类Person的属性
			getComparable(); //对于外部类中不重名结构,可以直接调用,前面省略Person.this【注意:不是this,此时this指代的是内部类本身,而非外部类】
		}
	}
	
	//3. 关注点3:开发中局部内部类的使用
	//局部内部类的常见用法:希望调用方法的时候,完成一个实现了某个接口的"实现类",同时帮我造个该实现类的对象,并返回给我
	//例如:返回一个实现了Comparable接口的类的对象
	public Comparable getComparable(){
		//创建一个实现了Comparable接口的类:局部内部类【为什么不把这个方法放在外面?因为外面用不到】
		//方式一:
//		class MyComparable implements Comparable{
//			@Override
//			public int compareTo(Object o) {
//				// TODO Auto-generated method stub
//				return 0;
//			}
//		}
//		
//		return new MyComparable();
		
		//方式二:匿名类对象
		return new Comparable() {
			@Override
			public int compareTo(Object o) {
				// TODO Auto-generated method stub
				return 0;
			}
		};
	}
}

4. 注意:内部类也会生成字节码文件

在这里插入图片描述


4.1 局部内部类的小注意点

在局部内部类的方法中(比如:onClick())如果调用局部内部类所声明在的方法(比如:onCreate())中的局部变量(比如:number),要求此局部变量是final的

  • 【因为外部类、内部类都会生成对应的字节码文件,从作用域的角度讲,内部类中的方法自然可以调用外部类中的局部变量;但从文件的角度讲,显然是两个字节码文件,跨文件了,所以实际上相当于是把number的一个副本给了内部类让其使用,既然是副本,自然不允许修改(因为改了也没用,又不是把本身传了过来)】

  • 【这一要求的应用在移动端开发(Android)中很常见】

  • JDK7.0及以前版本,要求显式声明此局部变量为final

  • JDK8.0以后,可以省略final的声明(一旦调用,默认会给加上)

public class InnerClassTest {

	//仅作举例说明(模仿安卓开发中的方法)
	public void onCreate(){
		int number = 10;
		
		//这里new的就是一个匿名内部类的匿名对象
		button.setOnClickListener(new View.onClickListener(){
			public void onClick(){
				System.out.println("Hello");
				System.out.println(number); //调用内部类所在方法中的局部变量
			}
		});
	}

}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值