1、类中的结构:成员变量、成员方法、构造器、代码块、内部类
2、内部类的概念?
从名字上就能看出它的意思:声明在另一个类里面的类,称为内部类。
class Outer{//外部类
class Inner{//内部类
}
public void method(){
class InClass{ //内部类
}
}
}
3、根据声明的位置不同:
(1)成员内部类:在外部类中,但是在方法和代码块的外面,和外部类的成员变量等是并列关系。
A:静态成员内部类,有static修饰,简称静态内部类
B:非静态成员内部类,没有static修饰,简称成员内部类,也就是说很多时候说的成员内部类都是指非静态成员内部类。
(2)局部内部类:在外部类的方法体或代码块中,和局部变量是并列关系。
A:有给局部内部类取名字:有名字局部内部类
B:没有给局部内部类取名字:匿名局部内部类,简称匿名内部类
匿名对象:是一个对象没有赋值给一个变量。
匿名内部类,是类没有名字。
public class TestInner {
static class One{
}
class Two{
}
public static void main(String[] args) {
System.out.println("-------------匿名对象和有名对象-----------------");
new TestInner();//匿名对象
TestInner t1 = new TestInner();//有名字的对象
System.out.println("----------------------");
new TestInner().method();//匿名对象直接调用method方法
TestInner t2 = new TestInner();
t2.method();//有名字的对象,通过变量/对象名调用method方法
System.out.println("----------------------");
test(new TestInner());//匿名对象作为调用test方法的实参
TestInner t3 = new TestInner();
test(t3);//使用有名字的对象作为调用test方法的实参
System.out.println("------------匿名内部类和有名的局部内部类------------------");
class Three{//有名字局部内部类,它在main中声明的局部内部类,名字叫做Three
public void fun(){
System.out.println("内部类Inner的fun方法");
}
}
new Object(){
public void fun(){
System.out.println("匿名内部类的fun方法");
}
}; //这是一个匿名内部类,正因为它没有名字,直接在声明类的同时,把对象也创建了
//这里有个;,因为这个在声明类的同时,new对象了,是一个表达式了,单独的表达式不能存在,必须加;变成语句
}
public void method(){
System.out.println("method方法");
}
public static void test(TestInner t){
//....
}
}
4、匿名内部类
(1)为什么需要匿名内部类?
当某些时候我们需要编写一个“父类的子类”或“接口的实现类”时,这个类的代码比较简单,并且这个类只在这里用一次。
这样的时候,我们可以考虑使用匿名内部类,来减少.java的数量,并且还可以增强代码的内聚力。
为什么不用有名的局部内部类?因为如果这个类只用一次,还给它取个名字,还麻烦了,命名是一件挺难的事情。
(2)如何声明匿名内部类?
形式一:
new 父类(){
//匿名子类的成员列表
}
形式二:
new 父类(实参列表){
//匿名子类的成员列表
}
形式三
new 父接口(){
//匿名实现类的成员列表
}
说明:
A:形式一这种格式声明的匿名内部类,它是这个父类的子类,它指明了它的直接父类是谁。
并且表名子类的构造器首行用了super()调用了父类的无参构造
B:形式二这种格式声明的匿名内部类,它是这个父类的子类,它指明了它的直接父类是谁。
并且表名子类的构造器首行用了super(实参列表)调用了父类的有参构造
C:形式三这种格式声明的匿名内部类,它是这个接口的实现类,它指明了它实现了xx接口,它的直接父类是Object。
并且表名子类的构造器首行用了super()调用了父类Object的无参构造
(3)如何在匿名内部类中声明成员?
和其他类差不多,区别在于以下几点:
A:匿名内部类不能手动编写构造器。
因为类没有名字,写不了。只能使用默认的无参构造。
虽然new后面不是写匿名内部类的构造器名字,因为写不了,所以只能写父类或父接口的名字。
因为无法手动编写构造器,所以只能在父类或父接口名的后面通过()或(实参列表)的形式来表名子类构造器中用了父类的哪个构造器。
B:匿名内部类中不能声明静态成员:静态变量、静态方法、静态代码块等。
C:匿名内部类中倒是可以声明静态的常量 (一般也不会这么做)
在匿名内部类中,可以声明自己扩展的方法,或者重写父类或者实现父接口的方法。更多的时候都是重写父类或父接口的方法。
(4)如何使用匿名内部类中的成员?
A:调用匿名内部类扩展的自己的方法:
只能通过匿名内部子类的匿名对象直接调用扩展的方法。
B:可以把匿名内部类的对象,赋值给他的父类或父接口类型的变量,
构成多态引用,然后通过 父类或父接口类型的变量调用 匿名子类重写的方法。
public class TestAnonymousInner {
public static void main(String[] args) {
new Object(){
}; //这个匿名内部类的直接父类是Object
//这个匿名子类的构造器首行,相当于是用了super()调用了父类Object的无参构造
new Father(){
};//这个匿名内部子类的直接父类是Father
//这个匿名子类的构造器首行,相当于是用了super()调用了父类的无参构造
new Father(1){
};//这个匿名内部类的直接父类是Father
//这个匿名子类的构造器首行,相当于是用了super(实参列表)调用了父类的有参构造
new MyInter(){
};//这个匿名内部类的直接父类是Object,它实现类MyInter接口。
//这个匿名子类的构造器首行,相当于是用了super()调用了父类Object的无参构造
}
}
abstract class Father{
private int a;
public Father() {
}
public Father(int a) {
this.a = a;
}
}
interface MyInter{
}
5、有名字的局部内部类(开发中使用最少的)
(1)局部内部类的使用仅限于声明它的方法中使用,甚至在类声明前面都不能用。
(2)局部内部类中可以使用外部类的成员,包括私有的。
如果局部内部类在静态方法中,那么不能直接使用外部类的非静态成员。
(3)局部内部类可以访问所在方法的使用final声明的局部变量。
在JDK1.8之前,必须手动加final;
在JDK1.8之后,如果某个局部变量在局部内部类中使用了,自动加final。
面试题:
(1)局部内部类的对象是否能够在声明它的方法以外的地方使用?
可以
(2)怎么用
这个局部内部类的对象,必须通过方法返回,或者通过外部类的成员变量传递一下。
public class TestHasNameLocalInner {
private static int a = 1;
private int b = 1;
private static Object four;
public static void main(String[] args) {
Object obj = test();
System.out.println(obj.getClass());//这里使用Object类型表示,那么返回的对象只能调用Object里面的方法
fun();
System.out.println(four.getClass());//这里使用Object类型表示,那么返回的对象只能调用Object里面的方法
Father fang = fang();
fang.ff();
}
public static Object test(){
class Three {
//...
}
return new Three();
}
public static void fun(){
class Four{
//...
}
four = new Four();//用成员变量,存储Four的对象
}
public static Father fang(){
class Three extends Father {
//...
}
return new Three();
}
/*
在method1方法中,声明了一个有名字的局部内部类,内部类中声明了一个fun方法
*/
public static void method1(){
// One one;//这里在One声明的前面,不能使用
int num = 3;
class One{
public void fun(){
System.out.println("有名字的局部内部类1");
System.out.println("a = " + a);//直接使用外部类的成员,包括私有的
// System.out.println("b = " + b);//这里报错的原因,静态方法method1不能直接访问非静态的b,这个和内部类无关
System.out.println("num="+num);//直接访问外部类方法的局部变量
}
}
One one = new One();
one.fun();
}
public void method2(){
class Two{
public void fun(){
System.out.println("有名字的局部内部类1");
System.out.println("a = " + a);//直接使用外部类的成员,包括私有的
System.out.println("b = " + b);//直接使用外部类的成员,包括私有的
}
}
}
}
class Father{
public void ff(){
System.out.println("父类的方法");
}
}
6、静态内部类
(1)如何什么声明?
【修饰符】 class 外部类静态内部类{
【其他修饰符】 static class 静态内部类{
}
}
(2)作为类来说
A:它有自己的字节码文件
其实所有的类包括匿名内部类,和局部内部类都有自己的字节码文件。
对比一下几种内部类的字节码文件?
静态内部类:外部类名$静态内部类.class
有名字的局部内部类:外部类名$编号局部内部类.class 因为在不同的方法中,局部内部类可能重名,所以有编号+局部内部类名
匿名的局部内部类:外部类名$编号.class 因为匿名内部类没有名字,所以用编号代替。
B:成员
只有静态内部类可以声明静态成员,其他的内部类都不能声明静态成员,除非是静态常量。
除了匿名内部类不能手动编写自己的构造器以外,其他内部类都可以编写自己的构造器。
其他的成员,和之前讲的类的成员一样编写即可。
C:可以继承自己的父类,也可以实现自己的接口
(3)作为成员来说
静态内部类可以使用外部类的成员,包括私有的,但是不能使用外部类的非静态成员。
反过来,外部类也可以直接使用静态内部类的成员,包括私有的。
(4)静态内部类如果在外部类的外面是可见的话,也可以在外部类的外面使用
如果使用静态内部类的静态成员:外部类名.静态内部类名.静态成员
如果使用静态内部类的非静态成员:
外部类名.静态内部类名 变量 = new 外部类名.静态内部类名();
变量.非静态成员形式使用
如果使用某个静态内部类多次,那么可以简化它的形式,
内部类的完整名字是包.外部类.静态内部类
现在可以使用import 语句 + 静态内部类的简名称
public class TestStaticInner {
public static void main(String[] args) {
//在这里调用Inner的method()方法
//创建静态内部类的对象,再调用它的非静态方法
Outer.Inner in = new Outer.Inner();
in.method();
//在这里调用Inner的test()方法
Outer.Inner.test();
//Inner是作为Outer的静态成员存在的
//test方法是作为Inner的静态成员存在的
System.out.println("----------------");
//注意一下写法是基于上面有import语句
Inner in2 = new Inner();
in.method();
Inner.test();
}
}
class Outer{
private static int a = 1;
private int b = 1;
static class Inner{
private static int c = 2;
private int d = 2;
public void method(){
System.out.println("静态内部类的非静态方法");
System.out.println("a = " +a);
// System.out.println("b = " +b);//因为Inner是静态的,不能直接使用外部类的非静态成员
}
public static void test(){
System.out.println("静态内部类的静态方法");
}
}
public static void wai(){
System.out.println(Inner.c);//外部类也可以访问静态内部类的私有的成员
Inner in = new Inner();
System.out.println(in.d);//外部类也可以访问静态内部类的私有的成员
}
}
7、非静态的内部类
(1)如何什么声明?
【修饰符】 class 外部类静态内部类{
【其他修饰符】 class 非静态内部类{
}
}
(2)作为类来说
A:它有自己的字节码文件
对比一下几种内部类的字节码文件?
静态内部类:外部类名$静态内部类.class
非静态内部类:外部类名$非静态内部类.class
有名字的局部内部类:外部类名$编号局部内部类.class 因为在不同的方法中,局部内部类可能重名,所以有编号+局部内部类名
匿名的局部内部类:外部类名$编号.class 因为匿名内部类没有名字,所以用编号代替。
B:成员
只有静态内部类可以声明静态成员,其他的内部类都不能声明静态成员,除非是静态常量。
除了匿名内部类不能手动编写自己的构造器以外,其他内部类都可以编写自己的构造器。
其他的成员,和之前讲的类的成员一样编写即可。
C:可以继承自己的父类,也可以实现自己的接口
(3)作为成员来说
非静态内部类可以使用外部类的成员,包括私有的。
反过来,外部类也可以直接使用非静态内部类的成员,包括私有的。
(4)非静态内部类如果在外部类的外面是可见的话,也可以在外部类的外面使用
使用非静态内部类的非静态成员
public class TestNonStaticInner {
public static void main(String[] args) {
//如何使用Inner的method方法
//(1)必须先创建外部类的对象
Outer out = new Outer();
//(2)借助外部类的对象,创建非静态内部类的对象
// Outer.Inner in = out.new Inner(); //这段代码的目的是为了得到非静态内部类的对象
Outer.Inner in = out.getInner();//可以在外部类中提供一个方法,返回非静态内部类的对象,我们接收它
//(3)调用方法
in.method();
}
}
class Outer{
private static int a;
private int b;
class Inner{
private int c;
public void method(){
System.out.println("a = " + a);
System.out.println("b = " + b);
}
}
public void test(){
Inner in = new Inner();
System.out.println(in.c);//外部类可以直接使用内部类的私有的成员
}
public Inner getInner(){
return new Inner();
}
}