将一个类A定义在另一个类B里面,里面的那个类A就称为内部类,B则称为外部类。
遵循高内聚低耦合的面向对象开发总原则。便于代码维护和扩展。
根据内部类声明的位置(如同变量的分类)可以分为:
(1)成员内部类:
(2)局部内部类
如果成员内部类中不使用外部类的非静态成员,那么通常将内部类声明为静态内部类,否则声明为非静态内部类。
语法格式:
【修饰符】 class 外部类{
【其他修饰符】 【static】 class 内部类{
}
}
有static修饰的成员内部类叫做静态内部类。它的特点:
和其他类一样,它只是定义在外部类中的另一个完整的类结构
和外部类不同的是,它可以允许四种权限修饰符:public,protected,缺省,private
只可以在静态内部类中使用外部类的静态成员
其实严格的讲(在James Gosling等人编著的《The Java Language Specification》)静态内部类不是内部类,而是类似于C++的嵌套类的概念,外部类仅仅是静态内部类的一种命名空间的限定名形式而已。所以接口中的内部类通常都不叫内部类,因为接口中的内部成员都是隐式是静态的(即public static)。例如:Map.Entry。
没有static修饰的成员内部类叫做非静态内部类。非静态内部类的特点:
和其他类一样,它只是定义在外部类中的另一个完整的类结构
和外部类不同的是,它可以允许四种权限修饰符:public,protected,缺省,private
在外部类的静态成员中不可以使用非静态内部类哦
在外部类的外面必须通过外部类的对象才能创建非静态内部类的对象(通常应该避免这样使用)
package com.atguigu.inner.member;
public class TestMemberInnerClass {
public static void main(String[] args) {
Outer.outMethod();
System.out.println("-----------------------");
Outer out = new Outer();
out.outFun();
System.out.println("####################################");
Outer.Inner.inMethod();
System.out.println("------------------------");
Outer.Inner inner = new Outer.Inner();
inner.inFun();
System.out.println("####################################");
Outer outer = new Outer();
// Outer.Nei nei = outer.new Nei();
Outer.Nei nei = out.getNei();
nei.inFun();
}
}
class Outer{
private static String a = "外部类的静态a";
private static String b = "外部类的静态b";
private String c = "外部类对象的非静态c";
private String d = "外部类对象的非静态d";
static class Inner{
private static String a ="静态内部类的静态a";
private String c = "静态内部类对象的非静态c";
public static void inMethod(){
System.out.println("Inner.inMethod");
System.out.println("Outer.a = " + Outer.a);
System.out.println("Inner.a = " + a);
System.out.println("b = " + b);
// System.out.println("c = " + c);//不能访问外部类和自己的非静态成员
// System.out.println("d = " + d);//不能访问外部类的非静态成员
}
public void inFun(){
System.out.println("Inner.inFun");
System.out.println("Outer.a = " + Outer.a);
System.out.println("Inner.a = " + a);
System.out.println("b = " + b);
System.out.println("c = " + c);
// System.out.println("d = " + d);//不能访问外部类的非静态成员
}
}
class Nei{
private String a = "非静态内部类对象的非静态a";
private String c = "非静态内部类对象的非静态c";
public void inFun(){
System.out.println("Nei.inFun");
System.out.println("Outer.a = " + Outer.a);
System.out.println("a = " + a);
System.out.println("b = " + b);
System.out.println("Outer.c = " + Outer.this.c);
System.out.println("c = " + c);
System.out.println("d = " + d);
}
}
public static void outMethod(){
System.out.println("Outer.outMethod");
System.out.println("a = " + a);
System.out.println("Inner.a = " + Inner.a);
System.out.println("b = " + b);
// System.out.println("c = " + c);
// System.out.println("d = " + d);
Inner in = new Inner();
System.out.println("in.c = " + in.c);
}
public void outFun(){
System.out.println("Outer.outFun");
System.out.println("a = " + a);
System.out.println("Inner.a = " + Inner.a);
System.out.println("b = " + b);
System.out.println("c = " + c);
System.out.println("d = " + d);
Inner in = new Inner();
System.out.println("in.c = " + in.c);
}
public Nei getNei(){
return new Nei();
}
}
静态内部类 | 非静态内部类 | ||
类角色 | 字节码文件 | 外部类名$内部类名 | 相同 |
修饰符 | public,缺省,abstract,final | 相同 | |
父类或父接口 | 可以 | 相同 | |
可以包含的成员 | 所有成员 | ==不允许有静态成员== | |
成员角色 | 修饰符 | public、protected、缺省、private,final,static | 没有static |
依赖于外部类 | 依赖 | 相同 | |
依赖于外部类的对象 | 不依赖 | ==依赖== | |
使用 | 在外部类中使用内部类 | 没有限制 | 在外部类的静态方法等中不能使用非静态内部类 |
在内部类中使用外部类 | 静态内部类中不能使用外部类的非静态成员 | 没有限制 | |
在外部类的外面使用内部类的静态成员 | 外部类名.静态内部类名.静态成员 | ==没有== | |
在外部类的外面使用内部类的非静态成员 | 见下面的框1 | 见下面的框2 | |
重名 | 外部类名.重名的成员名 | 外部类名.this.重名的成员 |
外部类名.静态内部类名 变量 = 外部类名.静态内部类名();
变量.非静态成员();
外部类名 变量1 = new 外部类();
外部类名.非静态内部类名 变量 = 变量1.new 非静态内部类名();
变量.非静态成员();
语法格式:
【修饰符】 class 外部类{
【修饰符】 返回值类型 方法名(【形参列表】){
【final/abstract】 class 内部类{
}
}
}
局部内部类的特点:
和外部类一样,它只是定义在外部类的某个方法中的另一个完整的类结构
编译后有自己的独立的字节码文件,只不过在内部类名前面冠以外部类名、$符号、编号。
局部内部类中还可以使用所在方法的局部常量,即用final声明的局部变量
示例代码:
package com.atguigu.inner.local;
public class TestLocalInner {
public static void main(String[] args) {
Runner runner = Outer.getRunner();
runner.run();
System.out.println("-------------------");
Outer.outMethod();
System.out.println("-------------------");
Outer out = new Outer();
out.outTest();
}
}
class Outer{
private static String a = "外部类的静态变量a";
private String b = "外部类对象的非静态变量b";
public static void outMethod(){
System.out.println("Outer.outMethod");
final String c = "局部变量c";
class Inner{
public void inMethod(){
System.out.println("Inner.inMethod");
System.out.println("out.a = " + a);
// System.out.println("out.b = " + b);//错误的,因为outMethod是静态的
System.out.println("out.local.c = " + c);
}
}
Inner in = new Inner();
in.inMethod();
}
public void outTest(){
class Inner{
public void inMethod(){
System.out.println("out.a = " + a);
System.out.println("out.b = " + b);//可以,因为outTest是非静态的
}
}
Inner in = new Inner();
in.inMethod();
}
public static Runner getRunner(){
class LocalRunner implements Runner{
@Override
public void run() {
System.out.println("LocalRunner.run");
}
}
return new LocalRunner();
}
}
interface Runner{
void run();
}
当我们在开发过程中,需要用到一个抽象类的子类的对象或一个接口的实现类的对象,而且只创建一个对象,而且逻辑代码也不复杂。那么我们原先怎么做的呢?
(1)编写类,继承这个父类或实现这个接口
(2)重写父类或父接口的方法
(3)创建这个子类或实现类的对象
这里,因为考虑到这个子类或实现类是一次性的,那么我们“费尽心机”的给它取名字,就显得多余。那么我们完全可以使用匿名内部类的方式来实现,避免给类命名的问题。
new 父类(【实参列表】){
重写方法...
}
//()中是否需要【实参列表】,看你想要让这个匿名内部类调用父类的哪个构造器,如果调用父类的无参构造,那么()中就不用写参数,如果调用父类的有参构造,那么()中需要传入实参
new 父接口(){
重写方法...
}
//()中没有参数,因为此时匿名内部类的父类是Object类,它只有一个无参构造
匿名内部类是没有名字的类,因此在声明类的同时就创建好了唯一的对象。
注意:
匿名内部类是一种特殊的局部内部类,只不过没有名称而已。所有局部内部类的限制都适用于匿名内部类。例如:
思考:这个对象能做什么呢?
(1)使用匿名内部类的对象直接调用方法
interface A{
void a();
}
public class Test{
public static void main(String[] args){
new A(){
@Override
public void a() {
System.out.println("aaaa");
}
}.a();
}
}
(2)通过父类或父接口的变量多态引用匿名内部类的对象
interface A{
void a();
}
public class Test{
public static void main(String[] args){
A obj = new A(){
@Override
public void a() {
System.out.println("aaaa");
}
};
obj.a();
}
}
(3)匿名内部类的对象作为实参
interface A{
void method();
}
public class Test{
public static void test(A a){
a.method();
}
public static void main(String[] args){
test(new A(){
@Override
public void method() {
System.out.println("aaaa");
}
});
}
}
用于实例变量的初始化等操作。
【修饰符】 class 类{
{
非静态代码块
}
【修饰符】 构造器名(){
// 实例初始化代码
}
【修饰符】 构造器名(参数列表){
// 实例初始化代码
}
}
所有非静态代码块中代码都是在new对象时自动执行。
执行一次构造器 就会执行一次代码块 并且一定是先于构造器的代码执行
package com.atguigu.code;
public class Student {
String name;
int age;
{
System.out.println("这是普通成员代码块");
name = "张三";
age = 30;
int m = 10;
}
public Student() {
System.out.println("--------Student()-------");
}
public Student(String name, int age) {
this.name = name;
this.age = age;
}
{
System.out.println("这是第二个代码块 ");
}
public void showInfo(){
System.out.println("name = "+name+",age = "+age);
}
}
public class StudentTest {
public static void main(String[] args) {
Student s1 = new Student();//代码块先于构造器执行
s1.showInfo();
System.out.println("---------------------");
new Student("张三",30);//调用一次构造器执行一次代码块
}
}
如果想要为静态变量初始化,可以直接在静态变量的声明后面直接赋值,也可以使用静态代码块。
在代码块的前面加static,就是静态代码块。
【修饰符】 class 类{
static{
静态代码块
}
}
每一个类的静态代码块只会执行一次。
静态代码块的执行优先于非静态代码块和构造器。
package com.atguigu.keyword;
public class Chinese {
// private static String country = "中国";
private static String country;
private String name;
{
System.out.println("非静态代码块,country = " + country);
}
static {
country = "中国";
System.out.println("静态代码块");
}
public Chinese(String name) {
this.name = name;
}
}
package com.atguigu.keyword;
public class TestStaticBlock {
public static void main(String[] args) {
Chinese c1 = new Chinese("张三");
Chinese c2 = new Chinese("李四");
}
}
静态代码块在类初始化时执行,只执行一次
非静态代码块在实例初始化时执行,每次new对象都会执行
© 著作权归作者所有
本文由 趣代码Blog 创作,采用 知识共享署名4.0 国际许可协议进行许可,本站文章除注明转载/出处外,均为本站原创或翻译,转载前请务必署名。