InnerClass
为什么使用内部类: 内部类是共享数据最简单的方式之一
内部类编译之后同样会生成.class文件.
其命名规则是: 外部类名字$内部类名字.class
class A {
int i = 7;
// 内部类
class C {
public void test() {
System.out.prinln(i);
}
}
}
成员内部类
能够共享外部类的所有(静态 + 非静态)成员(属性 和 方法).
如何创建长远内部类的对象:
Outer.Inner in = new Outer().new Inner();
public class TestMemberInner {
public static void main(String[] args) {
/*
内部类创建对象的方法:
(1) 外部类名.内部类名 变量名 = new 外部类名().new 内部类名()
Outer.Inner in1 = new Outer().new Inner();
(2) 先创建外部类的对象, 然后通过外部类的对象创建内部类对象.
Outer out = new Outer();
Outer.Inner in3 = out.new Inner();
in1 和 in2使用的是不同的外部类对象创建的, 所以他们在访问外部类的变量时, 实际上调用的是不同对象的方法.
对于外部类的方法, 从原理上来考虑, 一个类的所有对象, 调用的应该是同一个方法, 所以in1 和 in2调用的
内部类的方法是相同的, 不过这个没有啥讨论的意义.
但是对于静态成员变量, 这个应该是在类加载的时候声明的, 所以静态变量是属于类的, 而不属于某个对象.所以,
in1 和 in2 如果调用的是外部类的静态变量, 那么他们调用的是同一个静态变量.
而in3 和 in4用的是同一个外部对象创建的内部类对象.
*/
Outer.Inner in1 = new Outer().new Inner();
Outer.Inner in2 = new Outer().new Inner();
Outer out = new Outer();
Outer.Inner in3 = out.new Inner();
Outer.Inner in4 = out.new Inner();
}
}
class Outer { // 外部类
int a = 3; // 成员变量
static int b = 5; // 静态变量
class Inner {
int x = 5;
public void test() {
/*
对于a变量是否允许在内部类中调用:
test()是内部类的一个普通的方法,想要调用这个
方法就必须先实例化对象.要实例化Inner对象,就
必须实例化Outer对象,所以在调用这个方法的时候,
Outer对象已经存在, 并通过这个对象来实例化内
部类Inner.所以Outer类中的变量a是可以在内部类中
使用的.
*/
System.out.println(a); // true
System.out.println(b); // true
System.out.println(x); // true
}
}
}
有的地方把static声明的变量称为静态变量
, 有的地方也称为静态成员变量
.就这次培训来说, 用的是静态变量
静态内部类
能够共享外部类的静态成员(属性 + 方法). 虽然这个类是静态的, 但是仍然可以创建对象.我认为静态内部类的static标记, 是说明: 这个类对外部类的访问能力只限于静态的成员
如何创建对象
Outer.Inner in = new Outer.Inner();
静态内部类能够共享的只有外部类的静态成员, 静态成员还需要用内部类共享吗?
不需要, 可以直接用类名访问, 那么静态内部类就不是为了共享成员的.
它的存在,是为了描述类和类的归属关系的.
public class TestStaticInner{
public static void main(String[] args){
Outer.Inner in1 = new Outer.Inner();
Outer.Inner in2 = new Outer.Inner();
}
}
class Outer{//蛤蜊壳 = 房东
int a = 3;
static int b = 5;
static class Inner{//寄居蟹 = 房客
int x = 7;
static int y = 9;
public void test(){
//静态内部类的禁止访问外部类的非静态成员
System.out.println(a);//false
System.out.println(b);//true
// 内部类的非静态方法当然可以访问类体的非静态成员
// 但是内部类的静态方法不能访问内部类的非静态成员
System.out.println(x);//true (取决于test()是否是非静态的)
System.out.println(y);//true
}
}
}
局部内部类
局部就是在方法体的内部 或者 方法的参数, 比如局部变量
如果定义在静态方法中, 只能共享外部类的静态成员
如果定义在非静态方法中, 能够共享外部类的所有成员
局部内部类访问它所在方法中的局部变量必须是final
另外, 还有其所在的外部类的那个方法中的局部变量.只是JDK1.8之前, 必须手动加final, 8.0开始可以不加, 但是在编译阶段会自动加上.
根本原因: 因为局部内部类对象的生命周期比局部变量的生命周期长.当局部变量所处的函数执行积极而术后就已经死亡了, 不存在了, 但是局部内部类对象还可能一直存在(只要有人还引用该对象), 这样就会出现了一个悲剧的结果, 局部内部类对象访问一个已经不存在的局部变量.
解决办法: 用final修饰局部变量, 这是因为局部内部类最终会被编译为一个单独的类, 其所访问的final局部变量在编译时会成为这个类的属性(即在局部内部类中生成一个该局部变量的拷贝).通过将final局部变量复制一份, 复制品直接作为方法内部类中的数据成员, 由于被final修饰的变量赋值后不能在修改, 所以就保证了复制品与原始变量的一致.
public class TestLocalInner{
public static void main(String[] args){
}
}
class Outer{
int a = 3;
static int b = 5;
public void gogo(int c){ //参数
int d = 7; //局部变量
class Inner{ //局部内部类
int x = 9;
public void test(){
System.out.println(a);//true (取决于gogo是否是非静态的)
System.out.println(b);//true
System.out.println(c);//true (取决于JDK版本 8.0开始就行)
System.out.println(d);//true (取决于JDK版本 8.0开始就行)
System.out.println(x);//true!!
}
}
Inner in = new Inner();
in.test();
}
}
如何创建对象:
注意位置限定, 创建局部内部类的语句必须在类定义完成之后, 所在方法结束之前!
Inner in = new Inner();
匿名内部类
如果生个孩子 就是为了拿去卖钱的 就不需要给孩子起名字…
现实当中 很多时候 我们自己的名字根本不重要, 反而父母或者长辈的名字才重要…
局部内部类不但可以实现接口, 还可以继承父类.
// 用于实现接口
new 接口() {
抽象方法的具体实现
}
// 用于继承父类
new 父类(传参) {
抽象方法的具体实现
}
import java.util.*;
public class TestAnonyInner{
public static void main(String[] args){
Set<Integer> set = new TreeSet<>(new Comparator<Integer>(){
@Override
public int compare(Integer i1,Integer i2){
return i2.compareTo(i1);
}
});
Collections.addAll(set,55,33,44,11,22,22);
//我要降序!
System.out.println(set);
}
}
/*
class X implements Comparator<Integer>{
@Override
public int compare(Integer i1,Integer i2){
return i2.compareTo(i1);
}
}
*/
// lamda表达式也可以将内部类在进行简化
import java.util.*;
public class TestAnonyInnerPlus{
public static void main(String[] args){
Set<Integer> set = new TreeSet<>((i1,i2) -> i2.compareTo(i1));
Collections.addAll(set,55,33,44,11,22,22);
//我要降序!
System.out.println(set);
}
}
匿名内部类能够共享外部类的那些成员?
取决于定义它的位置, 可能等价于上述三种的某一种:
定义在类体当中, 等价于成员内部类
定义在类体当中又有static修饰, 等价于静态内部类
定义在方法提当中, 等价于局部内部类
// 匿名内部类练习
public class TestAnonyInner2{
public static void main(String[] args){
//用普通写法 创建一个学生 作为Person的子类
Student stu = new Student();
stu.eat();
//想要用匿名内部类的语法创建一个老师对象
Person tea = new Person("孔子",2120){
@Override
public void eat(){
System.out.println("老师吃仙贝");
}
};
tea.eat();
}
}
class Student extends Person{
public Student(){
super("子贡",2100);
}
@Override
public void eat(){
System.out.println("学生吃饺子");
}
}
abstract class Person{
String name;
int age;
public Person(String name,int age){
this.name = name;
this.age = age;
}
public abstract void eat();
}
共享数据的常用方式
一个凄美的爱情故事
故事的主角是一对小情侣 一个小男孩和一个小女孩
他们一起去看电影 然后买了两张电影票之后
钱不太够了 于是只买了一杯大可乐
但是呢 找服务员要了两根吸管 他们共享这一杯可乐… 广寒宫 = 电影院
貂蝉 = 可乐
吕布 = 小男孩
董卓 = 小女孩
test() = drink()
-
使用静态变量
public class TestShareData1{ public static void main(String[] args){ 吕布 lxb = new 吕布(); 董卓 ddz = new 董卓(); lxb.test(); ddz.test(); } } class 广寒宫{ static Object 貂蝉 = new Object(); } class 吕布{ public void test(){ System.out.println(广寒宫.貂蝉); } } class 董卓{ public void test(){ System.out.println(广寒宫.貂蝉); } }
-
使用参数传递(传参不要只会依赖构造方法, 要用setter)
public class TestShareData2{ public static void main(String[] args){ Object 貂蝉 = new Object(); 吕布 lxb = new 吕布(貂蝉); 董卓 ddz = new 董卓(貂蝉); lxb.test(); ddz.test(); } } class 吕布{ Object 夫人; public 吕布(Object 夫人){ this.夫人 = 夫人; } public void test(){ System.out.println(夫人); } } class 董卓{ Object 夫人; public 董卓(Object 夫人){ this.夫人 = 夫人; } public void test(){ System.out.println(夫人); } }
-
使用内部类
public class TestShareData3{ public static void main(String[] args){ 广寒宫 g = new 广寒宫(); 广寒宫.吕布 lxb = g.new 吕布(); 广寒宫.董卓 ddz = g.new 董卓(); lxb.test(); ddz.test(); } } class 广寒宫{ Object 貂蝉 = new Object(); class 吕布{ public void test(){ System.out.println(貂蝉); } } class 董卓{ public void test(){ System.out.println(貂蝉); } } }