文章目录
内部类,就是在 一个类的内部 声明 另一个类,编译后同样会生成.class文件。
内部类就相当于外部类的成员,因此可以直接访问外部类的任何属性和方法,
包括private修饰的变量和方法,当然,外部类也可以访问内部类的属性和方法,
在编写swing程序时,内部类出现的频率比较高(比如事件的监听器都是接口,用的时候要匿名内部类)。
一、成员内部类:
1.成员内部类 就是 外部类 的 一个 成员变量;
2.成员内部类 中 不能有静态声明;
3.内外互相访问的方法:
①外部类 直接访问 成员内部类,创建 成员内部类 的对象(最简单最常用的)
Inner inner = new Inner(“zhangsan”, 30);
②如果 成员内部类 访问权限为非private,main中直接访问 成员内部类 (用得较少)
Outer outer = new Outer();
Outer.Inner inner = outer.new Inner(参数);
③成员内部类 可以直接访问 外部类 所有的数据,也可以 类名.this
//这是一个Outer类,其中有一个属性是成员内部类
public class Outer{
//外部类自己的属性
private int i;
public void setI(int i) {
this.i = i;
}
public int getI() {
return i;
}
//外部类自己的方法
public void outerMethod1(){
System.out.println("outerMethod1方法的使用");
}
//外部类调用内部类的方法
public void outerMethod2(){
//先创建内部类对象,再调用
Inner inner = new Inner("zhangsan", 30);
System.out.println(inner);
inner.innermethod1();
}
//成员内部类---->类似于外部类的属性
class Inner{
private String name;
private int age;
public Inner(String name,int age){
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
@Override
public String toString() {
return "Inner [name=" + name + ", age=" + age + "]";
}
//内部类自己的方法
public void innermethod1(){
System.out.println("innerMethod1...");
}
//内部类调用外部类
public void innermethod2(){
System.out.println("调用外部类的成员方法");
//方法一:直接调用
outerMethod2();
//方法二:Outer.this调用
Outer.this.outerMethod2();
/*分析:在成员内部直接访问外部类的成员,其实是一个成员访问了另外一个成员,
一个非静态的成员访问非静态的成员,前面省略了this,
那么在非静态成员内部类中,调用外部类的其它非静态成员
是否也省略了this?是的
但是,如果直接用this会认为是内部类的当前对象
想要指定是外部类的当前对象,用外部类的类名.this
*/
}
}
}
//main方法测试代码:
public class 成员内部类 {
public static void main(String[] args) {
Outer outer = new Outer();
outer.outerMethod1();
System.out.println("=======================");
outer.outerMethod2();
System.out.println("=======================");
/*现在这个成员内部类访问权限是默认的,那么在 同一包下
* 就可以直接访问,现在我们来看如何直接创建成员内部类的对象
*/
Outer.Inner inner = outer.new Inner("lisi",40);
System.out.println(inner);
inner.innermethod1();
System.out.println("=======================");
inner.innermethod2();
}
}
二、静态内部类:
1.static 修饰类:
在java中如果一个类被声明为静态的,那么只有一种情况,即这个类是静态内部类,
因为外部类不能被声明为静态的。
①静态内部类 跟 静态方法 一样,
只能访问 静态的成员变量和方法,不能访问 非静态的方法和属性,
但是 普通内部类 可以访问 外部类 任意的成员变量和方法。
②静态内部类 可以声明 普通成员变量和方法,而 普通内部类 不能声明 static 成员变量和方法。
③静态内部类可以单独初始化::
Inner inner = new Outer.Inner();
普通内部类必须先初始化外部类:
Outer outer = new Outer();
Outer.Inner inner = outer.new Inner();
静态内部类使用场景一般是 当外部类需要使用内部类,而内部类无需外部类资源,
并且内部类可以单独创建的时候会考虑采用静态内部类的设计。
2.静态内部类:
1.静态内部类 可以看做 静态成员属性/方法;
2.静态内部类 中只能访问外部类的 静态成员,外部类则可以访问静态内部类的全部;
3.静态内部类 可以声明 普通成员变量和方法 和 静态成员变量和方法;
4.内外互相访问的方法:
①外部类直接访问静态内部类(简单常用)
Inner inner = new Inner();
②如果内部类非private main中可以直接创建对象
Outer.Inner inner = new Outer.Inner();
//这是一个Outer1类,其中有一个静态内部类
public class Outer1{
//外部类普通方法
public void test1(){
System.out.println("hello");
}
//外部类静态方法
public static void test2(){
System.out.println("world");
}
//外部类访问静态内部类的普通方法
public void test3(){
//Inner.test3();这是错误的,调用静态内部类的方法依旧需要new出对象
Inner inner = new Inner();
inner.innertest1();
}
//静态内部类 ---->类似于静态成员
static class Inner{
//静态内部类普通方法
public void innertest1(){
System.out.println("innertest1");
}
//静态内部类中只能访问外部类的静态成员--->静态的只能访问静态的
public void innertest2(){
test2();
}
}
}
//main方法测试代码:
public class 静态内部类 {
public static void main(String[] args) {
Outer1 outer1 = new Outer1();
outer1.test3();
System.out.println("================");
Outer1.Inner inner = new Outer1.Inner();
inner.innertest1();
System.out.println("=====================");
inner.innertest2();
}
}
P.S.static 和 final关键字复习:
(1)static 静态
①修饰类:静态内部类
②修饰成员变量:称之为类变量,静态变量
类变量 可以直接通过 类名.类变量名 引用,而不用实例化对象再引用
当然也可以实例化对象再引用
但是不管怎么引用,都共享这一个数据,一处修改,其他全变
③修饰成员方法:称之为类方法,静态方法
类方法 可以直接通过 类名.类方法名 引用,而不用实例化对象再引用
当然也可以实例化对象再引用
静态方法只能调用静态变量和静态方法,而且不能使用this关键字
非静态方法可以调用非静态变量和静态变量,也可以调用非静态方法和静态方法
(ps:为什么静态方法不能使用this关键字?
答:this关键字 代表的是 当前对象的引用,
静态方法随着类的加载而加载,静态方法是优先于对象创建而存在的
当我们加载一个类的时候,对象还没有存在,而this代表的是对象的引用,
试问当我们加载类的时候对象都没有,又怎么可以使用this呢?
答案显而易见:this是指当前对象,静态方法是通过类调用的方法,不需要实例化,
既然不需要实例化,就没有当前对象,既然没有当前对象,就不会有this。)
④修饰代码块{}:称之为 静态代码块
静态代码块:用于初始化静态变量,以及其他初始化工作,
静态代码块在初次实例化时调用,并且尽调用一次,而且在非静态代码块之前调用,
静态代码块不能调用非静态变量和非静态方法。
非静态代码块:每次实例化都会调用,而且在构造方法之前调用。
执行顺序:父类静态块>子类静态块>父类动态快>父类构造>子类动态快>子类构造
(2)final 最终
①修饰类:表示不能在被继承(断子绝孙类)
所以,只能用于修饰普通类,不能用于修饰 抽象类、接口
②修饰变量:属性(成员变量)、局部变量、方法参数
---->属性(成员变量):包括值变量(赋一次值)和引用变量(赋一次地址),
要么自己就有初值,要么构造函数赋初值,系统不会对final的变量默认初值
---->局部变量:包括值变量(赋一次值)和引用变量(赋一次地址),main中,只能赋值一次,不能再改
③修饰方法:表示不能在子类中被覆盖,即不能修改。
public class M{
private int i;
private final int b=1;
public M(int i) {
this.i = i;
}
public M() {
}
public int getI() {
return i;
}
public void setI(int i) {
this.i = i;
}
}
public class Test11 {
public static void main(String[] args) {
final M m=new M(10);//main中的引用变量,引用变量不可以再修改
//m=new M(20); 不可以
m.setI(1000);//可以的,因为类属性i并没有被final修饰
}
}
ps:水火不容之:final与abstract,private与abstract
三、局部内部类:
局部内部类 一般声明在某个函数的内部,只在该函数内有效,
用在 抽象类 和 接口 中较多,直接对其进行实现。
1.局部内部类 举例中 需要用到的 抽象类 和 接口:
①抽象类Shape:
package 内部类;
public abstract class Shape {
private Point location;
public Shape(Point location){
this.location = location;
}
public Shape(){}
public Point getLocation() {
return location;
}
public void setLocation(Point location) {
this.location = location;
}
public abstract double getArea();
public abstract double getLength();
public abstract void draw();
public void printLocation(){
System.out.println("图形的位置:"+location.getX()+","+location.getY());
}
public void printData(){
System.out.println(location.getX()+location.getY());
}
}
②类Point:
package 内部类;
public class Point {
private double x,y;
public Point(){}
public Point(double x, double y) {
this.x = x;
this.y = y;
}
public double getX() {
return x;
}
public void setX(double x) {
this.x = x;
}
public double getY() {
return y;
}
public void setY(double y) {
this.y = y;
}
public double getDistance(){
return Math.sqrt(x*x+y*y);
}
public double getDistance(Point p){
return Math.sqrt((this.x-p.x)*(this.x-p.x)+(this.y-p.y)*(this.y-p.y));
}
}
③接口CanFly:
interface CanFly{
void fly();//public abstract void fly();
}
④接口CanFly的实现:
class Bird implements CanFly{
public void fly() {
System.out.println("bird fly....");
}
}
class Plane implements CanFly{
public void fly() {
System.out.println("plane fly...");
}
}
class Person1{
/*
* 该方法参数类型是一个接口,调用该方法时要传递的是
* 实现了该接口的类的对象
*/
public void toFly(CanFly f1){
f1.fly();
}
public CanFly getOneCanFly(){
//该方法的返回值类型是接口,
//返回的是实现了该接口的类的实例对象
//return new Bird();
return new Plane();
}
}
⑤接口CanFly实现的测试:
package 内部类;
public class 接口 {
public static void main(String[] args) {
Bird bird = new Bird();
bird.fly();
Plane p1 = new Plane();
p1.fly();
System.out.println("=========================");
//父类的引用引用了子类的实例
CanFly fly1 = new Bird();
fly1.fly();//调用实现了接口的类的方法
CanFly fly2 = new Plane();
fly2.fly();
System.out.println("===========================");
Person1 person1 = new Person1();
person1.toFly(bird);
System.out.println("==================");
person1.getOneCanFly().fly();
}
}
2.局部内部类:
①方法返回抽象类,用到局部内部类:
class Persony{
//一个方法,返回抽象类,所以需要在方法继承抽象类,再返回
public Shape getOneShape(){
/*
* 函数的返回值类型是一个 抽象类
* 返回的一定是该抽象类子类的实例
* 只想在这个函数中使用
*/
class Line extends Shape{
private Point last;//起点
private Point end;//终点
public Line(){}
public Line(Point last,Point end){
this.last = last;
this.end = end;
}
public double getArea() {return 0;}
public double getLength() {
return last.getDistance(end);
}
public void draw() {
System.out.println("line draw...");
}
}
return new Line(new Point(5,5),new Point(10,10));
}
②方法返回接口,用到局部内部类:
//一个方法,返回接口,所以需要在方法中实现接口,再返回
public CanFly getOneCanFly(){
/*
* 看到函数的返回值类型是接口,返回的是实现了该接口的类的对象
* 因为在这里可能只有这个函数中使用所以
* 我们可以直接在函数内部创建类
*/
class Bird implements CanFly{
public void fly() {
System.out.println("bird..fly...");
}
}
//这个类声明在了一个函数的内部,这就是局部内部类,只在该函数内有效
return new Bird();
}
}
//main方法测试代码:
public class 局部内部类 {
public static void main(String[] args) {
Persony person = new Persony();
person.getOneCanFly().fly();
System.out.println("================");
Shape s = person.getOneShape();
s.draw();
System.out.println(s.getLength());
}
}
四、匿名内部类:
匿名内部类 是 局部内部类 的一种特殊情况
1.什么时候用?
已经知道父类,获取其子类的实例对象
已经知道接口,获取实现了该接口的类的对象
匿名类在用的时候必须是直接获取该匿名类的对象
2.怎么用?怎么样获取对象呢?
公式:
new 父类or接口()
{
子类的实现or实现了该接口的类的实现
}
公式得到的是匿名类的对象,
实现了该接口(继承了该抽象类)的类的实例对象,
只不过这个类是没有名字的
class PersonZ{
//已经知道接口,获取实现了该接口的类的对象
public CanFly getOneCanFly(){
//返回的类型是接口,那么要获取实现了该接口的类的实例
return new CanFly(){
@Override
public void fly() {
System.out.println("bird fly...");
}
};
}
public Shape getOneShape(){
/* 已经知道父类,获取其子类的实例对象
* 可以直接使用匿名类的对象
* 返回值类型是一个抽象类,那么返回的是该抽象类子类的实例
*/
return new Shape(){
@Override
public double getArea() {return 0;}
public double getLength() {return 0;}
@Override
public void draw() {
System.out.println("draw ..shape...");
}
};
}
}
//main方法测试代码:
package 内部类;
public class 匿名内部类 {
public static void main(String[] args) {
PersonZ pz = new PersonZ();
pz.getOneCanFly().fly();
System.out.println("=====================");
pz.getOneShape().draw();
}
}
class Personw{
public void playFly(CanFly fly){
fly.fly();
}
public void playDraw(Shape shape){
shape.draw();
}
}
//main方法测试代码:
package 内部类;
public class 匿名内部类2 {
public static void main(String[] args) {
Personw person = new Personw();
//已知道接口,获取其实现类的对象,可以直接用匿名类的对象
/* 调用方法时,发现参数是一个接口
* 那么传递的就是实现了该接口的类的对象
*/
person.playFly(new CanFly() {
@Override
public void fly() {
System.out.println("plane ...flying...");
}
});
System.out.println("======================");
//已知父类,获取其子类的对象,符合条件可以用
/*参数是一个抽象类,那么要传递的是该类子类的对象
*/
person.playDraw(new Shape(){
public double getArea() {return 0;}
public double getLength() { return 0;}
public void draw() {
System.out.println("drawing...shape...");
}
});
}
}
3.上面的公式 对父类和接口 看起来是一样,但是要注意有区别
new 父类(可以给父类的构造函数传递参数){子类实现}
接口不存在这个问题,接口是没有构造函数的。
注意:在匿名内部类中访问局部变量,该变量必须声明为final
但在JDK8之后,这个限制被取消了
package 内部类;
public class 父类和接口的区别 {
public static void main(String[] args) {
Student student = new Student();
//父类构造函数传参数
student.drawShape(new Shape1(10,10){
public void draw() {
System.out.println("drawing .shape...");
}
});
//在内部类中访问局部变量,该变量必须声明为final
final double x = 10;
final double y = 10;
student.drawShape(new Shape1(x,y){
public void draw() {
System.out.println("drawing .shape...location is:"+x+","+y);
}
});
System.out.println("=========================");
student.getOneShape(20, 20).draw();
}
}
class Student{
public void drawShape(Shape1 shape){
shape.draw();
}
//在内部类中访问局部变量,该变量必须声明为final
public Shape1 getOneShape(final double x,final double y){
return new Shape1(x,y){
@Override
public void draw() {
System.out.println("draw shape location is:"+x+","+y);
}
};
}
}
abstract class Shape1{
private Point location;
//public Shape1(){}
public Shape1(Point location){
this.location = location;
}
public Shape1(double x,double y){
this(new Point(x,y));
}
//抽象方法
public abstract void draw();
}