Java 面向对象(九)内部类
一、概述
1、引入
类的成员包括:
1、属性:成员变量
2、方法:成员方法
3、构造器
4、代码块
5、内部类:成员内部类
其中 1、2是代表这类事物的特征
其中3、4是初始化类和对象用的
其中5协助完成2的功能的实现,表现
2、定义
内部类:将一个类 A 定义在另一个类 B 里面,里面的那个类 A 就称为 内部类,类 B 则称为 外部类。
3、分类
根据内部类的所在的位置不同:
(1)成员内部类:外部类中方法外;
(2)局部内部类:方法体内(也可以在代码块内)
二、成员内部类
1、概述及分类
成员内部类:定义在 类中方法外 的类。
(1)有 static 修饰的:静态成员内部类,简称为 静态内部类。
(2)没有static修饰的,非静态成员内部类,简称 成员内部类。
扩展:什么情况下会用到成员内部类(方法外声明的)?
当描述一个事物时,发现它的内部还有一个完整的结构需要用一个类来描述;
并且发现这内部的结构,如果独立存在是没有意义的,必须在这个外部类中才有意义。而且这个内部结构只为这个外部类服务。例如:Body身体,发现它内部还有完整的结果,如:心脏,发现心脏单独创建对象没有意义,只有在 Body 对象中才有意义,而且只为 Body 对象服务。
2、静态成员内部类(静态内部类)
(1)语法格式:
【修饰符】 class 外部类 【 extends 父类】 【implements 父接口们】{
【其他修饰符】 static class 内部类 【 extends 父类】 【implements 父接口们】{
}
}
注意:
① 只有成员内部类才能用 static 修饰,其他的外部类,局部内部类等够不可以用 static 修饰。
② 外部类,内部类的父类,父接口都没有关系,各是各自的。
(2)特点
① 静态内部类中,可以出现原本类中能够定义的所有的成员
属性:可以有静态属性和非静态属性;
方法:可以有静态方法和非静态方法,如果静态内部类是抽象类,还可定义抽象方法;
构造方法:有参方法、无参方法;
代码块:可以有静态代码块和非静态代码块;
内部类:允许,但很少再写内部类(臃肿)
② 静态内部类中不能使用外部类的非静态的成员;
③ 在外部类中,使用静态内部类,和使用其他的类一样的原则;
如果使用静态内部类的静态成员,直接“静态内部类名.”
如果使用静态内部类的非静态成员,直接“静态内部类对象名.”
④ 在外部类的外面,使用静态内部类
如果使用静态内部类的静态成员,直接“类名”
使用 外部类名.静态内部类名.静态方法;
使用 import 包.外部类名.静态内部类名;然后在代码中使用 “静态内部类名”
⑤ 静态内部类不会随着外部类的初始化一起初始化,而是要在使用到这个静态内部类才会初始化;
(3)总结
① 同级的来说静态的不能直接使用非静态的;
② 访问一个类的静态成员,用 “类名.” 即可;访问一个类的非静态成员,用 “对象名.” 即可。
③ 当使用到这个类时,这个类才会进行初始化;
Demo:
package com.java.test;
//import com.java.test.Outer.Inner; public class TestStaticInner {
public static void main(String[] args) {
// 访问内部类的静态方法
// Inner.test();//上面有导包语句,import 包.外部类名.静态内部类名; Outer.Inner.test();//外部类名.静态内部类名.静态方法(不使用导包语句) // 访问内部类的非静态方法
// Inner in = new Inner();//上面有导包语句,import 包.外部类名.静态内部类名;
Outer.Inner in = new Outer.Inner();
in.method(); Outer out = new Outer(); // 内部类不会初始化
out.outMethod(); // 该方法中用到内部类,内部类会进行初始化
}
}
class Outer{
private int i = 1;
private static int j = 2; static{
System.out.println("外部类的静态代码块");
} static class Inner{
static{
System.out.println("静态内部类的代码块");
} public void method(){
System.out.println("静态内部类的非静态方法");
//System.out.println(i);//错误,不能访问非静态的
System.out.println(j);
} public static void test(){
System.out.println("静态内部类的静态方法");
}
}
// 外部类访问内部类
public void outMethod(){
Inner in = new Inner();
in.method();//非静态方法,用对象名.访问 Inner.test();//静态方法,用类名.访问
}
}
3、非静态成员内部类
(1)语法格式:
【修饰符】 class 外部类 【 extends 父类】 【implements 父接口们】{
【其他修饰符】 class 内部类 【 extends 父类】 【implements 父接口们】{
}
}
(2)特点
① 在非静态内部类中,不能出现任何和 static 有关的声明;
② 在非静态内部类中,可以随意访问外部类的所有的成员,包括静态的和非静态的;
③ 在外部类的静态成员中,不能使用非静态的成员内部类
④ 在外部类的外面使用
第一步:先创建外部类的对象
第二步:要么通过外部类的对象,去创建内部类的对象
Outer out = new Outer();
Outer.Inner in = out.new Inner();
要么通过外部类的对象,去获取内部类的对象
Outer out = new Outer();
Outer.Inner in = out.getInner();
Demo:
public class TestNonStaticInner {
public static void main(String[] args) {
//Outer.Inner in = new Outer.Inner();//错误的 //在这里使用Inner,因为此时的Inner是Outer的非静态成员,所以需要用到Outer的对象
Outer out = new Outer();
//Outer.Inner in = out.new Inner();
Outer.Inner in = out.getInner();
in.method();
}
}
class Outer{
private int i = 1;
private static int j = 2; class Inner{
public void method(){
System.out.println("非静态内部类的非静态方法");
System.out.println(i);
System.out.println(j);
}
} public static void outTest(){
//Inner in = new Inner();//静态的方法不能访问非静态的成员
}
public void outMethod(){
Inner in = new Inner();
in.method();
} public Inner getInner(){
return new Inner();
}
}
案例:创建一个外部类,里面有一个抽象内部类,并有一个抽象方法,要求写一个类去继承该内部类。
代码:
public class Test {
public static void main(String[] args) {
MySub my = new MySub(new Outer());
my.test();
}
}
class Outer{
abstract class Inner{
public abstract void test();
}
}
class MySub extends Outer.Inner{
MySub(Outer out){
out.super();//需要外部类的对象,才能调用非静态内部类的构造器
} @Override
public void test() {
System.out.println("hello");
}
}
说明:
① 使用非静态内部类名时,可以使用:import 包.外部类名.内部类名; 或 外部类名.内部类名;
② 要调用非静态内部类的构造器,需要用到外部类的对象
③ 因为子类的构造器的首行一定要调用父类的构造器,默认调用父类的无参构造(需要借助外部类对象)
④ 继承抽象类,要重写抽象的抽象方法
注意:内部类仍然是一个独立的类,在编译之后会内部类会被编译成独立的 .class 文件,但是前面冠以外部类的类名和 $ 符号。如:Person$Heart.class
三、局部内部类
1、概述
局部内部类:定义在 类中方法内 的类,就是局部内部类。即只有当前所属的方法才能使用它,出了这个方法外面就不能使用。
(1)有名字的局部内部类:简称 局部内部类;
(2)没名字的局部内部类:简称 匿名内部类。
2、定义格式
局部内部类定义格式:
【修饰符】 class 外部类名称 {
【修饰符】 返回值类型 外部类方法名称(参数列表) {
【修饰符】 class 有名字的局部内部类名称 {
// ...
}
}
}
说明:如果子类调用的是父类的无参构造,那么()中实参列表不用写;如果子类调用的是父类的有参构造,那么就在()中传入实参列表
定义一个类的时候,权限修饰符规则:
(1)外部类: public / (default)
(2)成员内部类:public / protected / (default) / private
(3)局部内部类:不加任何修饰符
3、特点
(1)局部内部类的修饰符,只能有 abstract 或 final
(2)该类有作用域
(3)如果局部内部类在静态方法中,不能使用外部类的非静态成员
(4)在局部内部类中,可以使用当前局部内部类所在方法的局部变量,但是要求,这个局部变量必须是 final的常量。
在Java8时,如果某个局部变量被局部内部类使用了,会自动添加final变为常量,一旦变为常量,它的值就不能修改了。
为什么它要这么要求?加final
避免局部内部类对象被返回到外部类的外面使用时,访问不到这个局部变量,所以要把这个局部变量变为final的常量。(常量在方法区中)
原因:
① new出来的对象在堆内存当中。
② 局部变量是跟着方法走的,在栈内存当中。
③ 方法运行结束之后,立刻出栈,局部变量就会立刻消失。
④ 但是new出来的对象会在堆当中持续存在,直到垃圾回收消失。
Demo:
public class TestLocalInner {
public static void main(String[] args) {
Outer out = new Outer();
Father in = out.test();//在外部类的外面虽然不能使用局部内部类,但是可以得到它的对象
System.out.println(in.getClass());
in.method();//在这里仍然可以访问到这个a,那么这个a就不能存在栈中,得挪到方法区,变为常量
}
}
abstract class Father{
public abstract void method();
}
class Outer{
private int i = 1;//成员变量,实例变量,非静态成员变量
private static int j = 2;//成员变量,类变量,静态变量 public Father test(){
//Inner in = new Inner(); 现在还没有声明该类,不能使用 final int a = 10;//局部变量==>局部的常量 //局部内部类
class Inner extends Father{
public void method(){
System.out.println(i);
System.out.println(j);
System.out.println(a);
}
} Inner in = new Inner();
in.method(); return in;
} public void method(){
//Inner in = new Inner(); 超过了作用域
} public static void fun(){
//局部内部类
class Inner{
public void method(){
//System.out.println(i);//是因为fun方法是静态的
System.out.println(j);
}
}
}
}
四、匿名内部类【重要】
1、概述
匿名内部类:是内部类的简化写法。它的本质是一个 带具体实现的 父类或者父接口的 匿名的 子类对象。
开发中,最常用到的内部类就是匿名内部类了,以接口为例,当你使用一个接口时,都得做如下几步操作:
(1)定义子类
(2)重写接口中的方法
(3)创建子类对象
(4)调用重写后的方法
而匿名类就可以把以上四步合成一步。
前提:匿名内部类必须继承一个父类或者实现一个父接口
2、格式
定义格式:
new 父类名或者接口名(){
// 方法重写
@Override
public void method() {
// 执行语句
}
};
特殊:声明匿名内部类与创建它的对象是一起完成的,即匿名内部类只有唯一的对象。
3、使用方式
以接口为例,匿名内部类的使用。
定义接口:
public abstract class FlyAble{
public abstract void fly();
}
创建匿名内部类,并调用:
public class InnerDemo {
public static void main(String[] args) {
/*
1.等号右边:是匿名内部类,定义并创建该接口的子类对象
2.等号左边:是多态赋值,接口类型引用指向子类对象
*/
FlyAble f = new FlyAble(){
public void fly() {
System.out.println("我飞了~~~");
}
};
//调用 fly方法,执行重写后的方法
f.fly();
}
}
通常在方法的形式参数是接口或者抽象类时,也可以将匿名内部类作为参数传递。如下所示
public class InnerDemo2 {
public static void main(String[] args) {
/*
1.等号右边:定义并创建该接口的子类对象
2.等号左边:是多态,接口类型引用指向子类对象
*/
FlyAble f = new FlyAble(){
public void fly() {
System.out.println("我飞了~~~");
}
};
// 将f传递给showFly方法中
showFly(f);
}
public static void showFly(FlyAble f) {
f.fly();
}
}
将以上两步,也可以简化为一步:
public class InnerDemo3 {
public static void main(String[] args) {
/*
创建匿名内部类,直接传递给showFly(FlyAble f)
*/
showFly( new FlyAble(){
public void fly() {
System.out.println("我飞了~~~");
}
});
}
public static void showFly(FlyAble f) {
f.fly();
}
}
Java 面向对象(九)内部类的更多相关文章
- Java面向对象15——内部类
内部类(了解) 成员内部类 package oop.demon01.demon10; public class Outer { private int id = 10; pu ...
- JAVA面向对象-----局部内部类
局部内部类 局部内部类概述:包含在外部类的函数中的内部类称之为局部内部类. 访问:可以在包含局部内部类的方法中直接创建局部内部类的对象调用局部内部类的成员. 注意:局部内部类只能访问所在函数的fana ...
- LY.JAVA面向对象编程.内部类
2018-07-18 10:14:48 /* 内部类概述: 把类定义在其他类的内部,这个类就被称为内部类. 举例:在类A中定义了一个类B,类B就是内部类. 内部的访问特点: A:内部类可以直接访问外部 ...
- Java面向对象_内部类
概念:内部类就是类的内部定义的类 成员内部类格式如下:class Outer{ class Inner{} } 编译上述代码会产生两个文件:Outer.class和Outer$Inner.class ...
- Java面向对象 Object类 内部类
Java面向对象 Object类 内部类 知识概要: 一:Object类 二:内部类 匿名内部类的写法 1.Object O ...
- JAVA面向对象-----内部类的概述
JAVA面向对象-–内部类的概述s 将类定义在另一个类的内部则成为内部类.其实就是类定义的位置发生了变化. 在一个类中,定义在类中的叫成员变量,定义在函数中的叫成员函数,那么根据类定义的位置也可以分为 ...
- 如何讲清楚 Java 面向对象的问题与知识?(类与对象,封装,继承,多态,接口,内部类...)
写在最前面 这个项目是从20年末就立好的 flag,经过几年的学习,回过头再去看很多知识点又有新的理解.所以趁着找实习的准备,结合以前的学习储备,创建一个主要针对应届生和初学者的 Java 开源知识项 ...
- Java学习笔记二十九:一个Java面向对象的小练习
一个Java面向对象的小练习 一:项目需求与解决思路: 学习了这么长时间的面向对象,我们只是对面向对象有了一个简单的认识,我们现在来做一个小练习,这个例子可以使大家更好的掌握面向对象的特性: 1.人类 ...
- 深入java面向对象四:Java 内部类种类及使用解析(转)
内部类Inner Class 将相关的类组织在一起,从而降低了命名空间的混乱. 一个内部类可以定义在另一个类里,可以定义在函数里,甚至可以作为一个表达式的一部分. Java中的内部类共分为四种: 静态 ...
- Java面向对象----个人参考资料
Java面向对象 :什么是面向对象.类与对象.封装.构造方法.static关键字.继承.抽象类.接口.多态 一.什么是面向对象 1.面向过程思想 面向过程:(PO,Procedure Oriented ...
随机推荐
- Kafka数据安全性、运行原理、存储
直接贴面试题: 怎么保证数据 kafka 里的数据安全? 答: 生产者数据的不丢失kafka 的 ack 机制: 在 kafka 发送数据的时候,每次发送消息都会有一个确认反馈机制,确保消息正常的能够 ...
- SimpleDateFormat 中的yyyy-MM-dd HH:mm:ss.SSS说明
SimpleDateFormat sdf=new SimpleDateFormat("yyyy-MM-dd HH:mm:ss"); 转换的格式:yyyy是完整的西元年,MM是月份, ...
- flask框架搭建项目的具体步骤
flask是一个使用python编写的轻量级Web应用框架.与django不同,Django创建工程时,会直接构架好工程目录.而flask工程几乎是自己创建结构. 1.导入相关模块以及需要使用的模块: ...
- django认证系统-user对象(创建,改密,认证)
User对象 User对象是认证系统的核心.它们通常表示与你的站点进行交互的用户,并用于启用限制访问.注册用户信息和关联内容给创建者等.在Django的认证框架中只存在一种类型的用户,因此诸如'sup ...
- Biorhythms(信息学奥赛一本通 1639)
题目描述: 人生来就有三个生理周期,分别为体力.感情和智力周期,它们的周期长度为23天.28天和33天.每一个周期中有一天是高峰.在高峰这天,人会在相应的方面表现出色.例如,智力周期的高峰,人会思维敏 ...
- Spring Security教程之基于方法的权限控制(十二)
目录 1.1 intercept-methods定义方法权限控制 1.2 使用pointcut定义方法权限控制 1.3 使用注解定义方法权限控制 1.3.1 JSR-25 ...
- IBM X3650 m4 面板指示灯
- 【Python开发】Pycharm下的Anaconda配置
我的系统是Win 64位的,用的Python 3.5.1 ,最近在学机器学习,用到了Numpy这个科学计算库,网上查了之后,看到很多装Numpy出问题的情况,所以决定装Anaconda,简单一些,并且 ...
- 新添加的用户无法sudo解决方案
问题:xxx(用户名) is not in the sudoers file. This incident will be reported. 用户管理: 1.创建用户 (1)方法1 $ sudo a ...
- recttransform
转载自https://www.jianshu.com/p/4592bf809c8b 1.Anchor:子物体和父物体联系的桥梁,Anchors是由两个点确定的,他们就是AnchorMin以及Ancho ...