Java内部类成员\静态\局部\匿名

本文详细介绍了Java中的四种内部类:成员内部类、静态内部类、局部内部类和匿名内部类。成员内部类可以直接访问外部类的所有属性和方法。静态内部类可以单独初始化,只能访问外部类的静态成员。局部内部类通常在函数内部定义,仅在该函数内有效。匿名内部类用于快速实现已知接口或父类的子类,简化代码。
摘要由CSDN通过智能技术生成


内部类,就是在 一个类的内部 声明 另一个类,编译后同样会生成.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();
}
  • 3
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值