解析JavaSe的内部类

目录
  • 内部类
    • 1.内部类简介
    • 2.非静态内部类
    • 3.静态内部类
    • 4.匿名内部类
  • 总结

内部类

1. 内部类简介

(1) 内部类提供了更好的封装,可以把内部类隐藏在外部类之内,不允许同一个包中的其他类访问该类。

(2) 内部类成员可以直接访问外部类的私有数据,因为内部类被当成其外部类成员,同一个类的成员之间可以互相访问。但外部类不能访问内部类的实现细节,例如内部类的成员变量。

(3) 匿名内部类适合用于创建那些仅需要一次使用的类。

(4) 在java中内部类主要分为成员内部类(非静态内部类、静态内部类)、匿名内部类、局部内部类。

2. 非静态内部类

成员内部类是一种与Field、方法、构造器和初始化块相似的类成员,成员内部类分为两种:静态内部类和非静态内部类,使用static修饰的成员内部类是静态内部类,没有使用static修饰的成员内部类是非静态内部类。

  • 因为内部类作为其外部类的成员,所以可以使用任意访问控制符如private、protected和public等修饰;
  • 非静态内部类不能有静态方法、静态属性、静态初始化块;
  • 非静态内部类可以直接访问外部类的成员,但是外部类不能直接访问非静态内部类成员;
  • 外部类的静态方法、静态代码块中不能直接创建非静态内部类实例,访问内部类成员;
  • 非静态内部类的对象必须寄存在外部类的对象里,因此创建非静态内部类对象之前,必须先创建其外部类对象;

当在非静态内部类的方法内访问某个变量时,系统优先在该方法内查找是否存在该名字的局部变量,如果存在就使用该变量;如果不存在,则到该方法所在的内部类中查找是否存在该名字的成员变量,如果存在则使用该成员变量;如果不存在,则到该内部类所在的外部类中查找是否存在该名字的成员变量,如果存在则使用该成员变量;如果依然不存在,系统将出现编译错误:提示找不到该变量。

(1) 非静态内部类可以直接访问外部类的成员,但是外部类不能直接访问非静态内部类成员

public class OuterClass {
    private int a;
    public void test(){
        // 编译报错,因为外部类不能直接访问非静态内部类成员
        System.out.println(b);
        // 如需访问内部类的实例Field,需显式创建内部类对象
        InnerClass inner = new InnerClass();
        System.out.println(inner.b);
    }
    // 定义非静态内部类,使用private修饰符
    @Data
    private class InnerClass{
        private int b;
        public void info(){
            // 在非静态内部类里可以直接访问外部类的private成员
            System.out.println(a);
        }
    }
}

(2) 非静态内部类不能有静态方法、静态属性、静态初始化块

public class OuterClass {
    // 外部类Field
    private int a;
    // 定义非静态内部类,使用private修饰符
    @Data
    private class InnerClass{
        // 内部类Field
        private int b;
        private int c;
        // 编译报错,非静态内部类里面不能有静态属性
        private static int d;
        // 编译报错,非静态内部类里面不能有静态代码块
        static {
            System.out.println("非静态内部类里面不能有静态代码块");
        }
        // 编译报错,非静态内部类里面不能有静态方法
        public static void show(){
            System.out.println("非静态内部类里面不能有静态方法");
        }
    }
}

(3) 外部类的静态方法、静态代码块中不能直接创建非静态内部类实例,访问内部类成员

public class OuterClass {
    private int a;
    public static void test(){
        // 编译报错,外部类的静态方法中无法创建内部类实例
        InnerClass innerClass = new InnerClass();
    }
    static{
        // 编译报错,外部类的静态方法中无法创建内部类实例
        InnerClass innerClass = new InnerClass();
    }
    // 定义非静态内部类
    @Data
    private class InnerClass{
        private int b;
    }
}

在外部类的普通方法和静态方法中访问内部类成员

public class OuterClass {
    private int a;
    // 定义非静态内部类,使用private修饰符
    @Data
    private class InnerClass{
        private int b;
        public void info(){
            System.out.println("内部类的方法info()");
        }
    }
    // 外部类的代码块
    {
        InnerClass innerClass = new InnerClass();
        innerClass.info();
    }
    // 外部类的静态代码块
    static {
        OuterClass.InnerClass inner = new OuterClass().new InnerClass();
        inner.info();
    }
    // 外部类的普通方法
    public void test(){
        // 在外部类里使用非静态内部类时,与平时使用普通类并没有太大的区别
        InnerClass inner = new InnerClass();
        // 访问内部类的Filed
        System.out.println(inner.b);
        // 访问内部类的方法
        inner.info();
    }
    //  外部类的静态方法
    public static void test1(){
        // 外部类的静态方法、静态代码块中不能直接创建非静态内部类实例
        OuterClass.InnerClass inner = new OuterClass().new InnerClass();
        // 访问内部类的Filed
        System.out.println(inner.b);
        // 访问内部类的方法
        inner.info();
    }
    // 测试
    public static void main(String[] args) {
        OuterClass outerClass = new OuterClass();
        outerClass.test();
    }
}

3. 静态内部类

如果使用static来修饰一个内部类,则这个内部类就属于外部类本身,而不属于外部类的某个对象。static关键字的作用是把类的成员变成类相关,而不是实例相关,即static修饰的成员属于整个类,而不属于单个对象。

  • 静态内部类可以包含静态成员,也可以包含非静态成员。
  • 静态内部类不能访问外部类的实例成员,只能访问外部类的类成员;
  • 外部类不能直接访问静态内部类的成员,但可以使用静态内部类的类名作为调用者来访问静态内部类的类成员,也可以使用静态内部类对象作为调用者来访问静态内部类的实例成员。

(1) 静态内部类不能访问外部类的实例成员,只能访问外部类的类成员

public class OuterClass {
    private int a;
    private static int b;
    public void test(){
        System.out.println(a);
    }
    @Data
    private static class InnerClass{
        private int c;
        // 静态内部类中可以包括静态成员
        private static int d;
        public void info(){
            // 编译报错,静态内部类不能访问外部类的实例成员
            System.out.println(a);
        }
        public static void show(){
            // 静态内部类可以访问外部类的静态成员
            System.out.println(b);
        }
    }
}

(2) 外部类不能直接访问静态内部类的成员,但可以使用静态内部类的类名作为调用者来访问静态内部类的类成员,也可以使用静态内部类对象作为调用者来访问静态内部类的实例成员。

public class OuterClass {
    private int a;
    private static int b;
    public void test(){
        // 外部类不能直接访问静态内部类的成员
        // 可以使用静态内部类对象作为调用者来访问静态内部类的实例成员
        InnerClass innerClass = new InnerClass();
        innerClass.show();
        // 可以使用静态内部类的类名作为调用者来访问静态内部类的类成员
        InnerClass.show();
    }
    public static void main(String[] args) {
        OuterClass outerClass = new OuterClass();
        outerClass.test();
    }
    @Data
    private static class InnerClass{
        private int c;
        private static int d;
        public static void show(){
            System.out.println(b);
        }
    }
}

4. 匿名内部类

匿名内部类适合创建那种只需要一次使用的类,创建匿名内部类时会立即创建一个该类的实例,这个类定义立即消失,匿名内部类不能重复使用。

  • 匿名内部类必须继承一个父类,或实现一个接口,但最多只能继承一个父类,或实现一个接口;
  • 匿名内部类不能是抽象类,因为系统在创建匿名内部类时,会立即创建匿名内部类的对象。因此不允许将匿名内部类定义成抽象类;
  • 匿名内部类不能定义构造器,因为匿名内部类没有类名,所以无法定义构造器,但匿名内部类可以定义实例初始化块,通过实例初始化块来完成构造器需要完成的事情;
public interface Product {
     public int getPrice();
     public String getName();
}
public class Test {
    public void test(Product product){
        System.out.println("name:"+product.getName() +"-------"+"name:"+product.getPrice());
    }
    public static void main(String[] args) {
        Test test = new Test();
        test.test(new Product() {
            @Override
            public int getPrice() {
                return 12;
            }
            @Override
            public String getName() {
                return "苹果";
            }
        });
    }
}

Test类定义了一个test方法,该方法需要一个Product对象作为参数,但Product只是一个接口,无法直接创建对象,因此此处考虑创建一个Product接口实现类的对象传入该方法——如果这个Product接口实现类需要重复使用,则应该将该实现类定义成一个独立类;如果这个Product接口实现类只需一次使用,则可采用上面程序中的方式,定义一个匿名内部类。

总结

本篇文章就到这里了,希望能够给你带来帮助,也希望您能够多多关注我们的更多内容!

(0)

相关推荐

  • 详解Java匿名内部类

    匿名内部类: 先举个例子吧,给大家看一下什么是匿名内部类,Endeavor刚刚接触的时候,觉得哇哦,好奇怪的样子,这也太别扭了吧,不知道大家是什么感觉. 为了进行对比,先举一个正常的类方法调用的例子(大家应该都看的懂吧): 输出结果为: 接下来便开始说正题吧,匿名内部类,通过名字,想必大家就知道什么是匿名内部类了吧, 1.定义:就是没有名字的内部类(内部类之前介绍过了哦). 2.使用内部类有什么好处呢,一句话就概括了:简化书写,至于是怎么简化的,哪里简化了等下再说. 3.先说一下什么时候使用匿名

  • JAVA匿名内部类(Anonymous Classes)的具体使用

    目录 1.前言 2.匿名内部类 2.1 定义匿名内部类 2.2 匿名内部类的语法 3.访问作用域内的局部变量.定义和访问匿名内部类成员 4.匿名内部类实例 写在最后: 1.前言 匿名内部类在我们JAVA程序员的日常工作中经常要用到,但是很多时候也只是照本宣科地用,虽然也在用,但往往忽略了以下几点:为什么能这么用?匿名内部类的语法是怎样的?有哪些限制?因此,最近,我在完成了手头的开发任务后,查阅了一下JAVA官方文档,将匿名内部类的使用进行了一下总结,案例也摘自官方文档.感兴趣的可以查阅官方文档(

  • JavaSE学习之内部类及常用API

    目录 1.内部类 1.1 内部类概述 1.2 成员内部类 1.3 局部内部类 1.4 匿名内部类 1.5 匿名内部类在开发中的使用 2.常用API 2.1Math 2.2 System 2.3 Object 2.4 Array的排序 1.内部类 1.1 内部类概述 内部类:就是在一个类中定义一个类.例如:在一个类A内部定义一个类B,类B就被称为内部类 格式:public class 类名{修饰符 class 类名{}} 范例 public class Outer{ public class In

  • Java中的内部类你了解吗

    目录 成员内部类 1.定义 2.成员内部类的使用规则 3.成员内部类对象的创建: 4.内部类与静态域 静态内部类: 1.定义: 2.静态内部类的使用规则: 3.静态内部类对象的创建 成员内部类 VS 静态内部类 方法内部类: 1.定义: 2.方法内部类的使用规则: 3.注意形参的使用 匿名内部类(函数式编程) 总结 成员内部类 1.定义 成员内部类是直接定义在类中,不加任何修饰符的(特指不加static修饰的)的内部类,可以类比着成员变量来理解,如下面这个代码段中的Inn类就是一个成员内部类 p

  • Java的内部类总结

    目录 前言 一,成员内部类 1,成员内部类的特点 2,成员内部类的实现 3,内部类和外部类的关系 二,静态内部类 1,静态内部类的特点 2,比较成员内部类和静态内部类 3,静态内部类的实现 三,方法内部类 1,方法内部类的特点 2,方法内部类的实现 四, 匿名内部类 1,匿名内部类的特点 2,匿名内部类的实现 总结 前言 最近看了内部类后,总结一下,首先内部类嵌套在其他内部的类,根据出现的位置和关键字,可以分为以下四种类:成员内部类,静态内部类,方法内部类,匿名内部类,接下来就介绍下这四种类,记

  • 解析JavaSe的内部类

    目录 内部类 1.内部类简介 2.非静态内部类 3.静态内部类 4.匿名内部类 总结 内部类 1. 内部类简介 (1) 内部类提供了更好的封装,可以把内部类隐藏在外部类之内,不允许同一个包中的其他类访问该类. (2) 内部类成员可以直接访问外部类的私有数据,因为内部类被当成其外部类成员,同一个类的成员之间可以互相访问.但外部类不能访问内部类的实现细节,例如内部类的成员变量. (3) 匿名内部类适合用于创建那些仅需要一次使用的类. (4) 在java中内部类主要分为成员内部类(非静态内部类.静态内

  • 解析JavaSe的抽象类和接口

    目录 1. 抽象类和抽象方法 2. 相关面试题 1.抽象类必须要有抽象方法吗? 2.普通类和抽象类有哪些区别? 3.抽象类能使用 final 修饰吗? 3. 接口 3.1 接口中的常量 3.2 接口中的方法 3.2.1 接口中的普通方法 3.2.2 接口中的静态方法 3.2.3 接口中的默认方法 3.3 接口中的接口和枚举类 3.4 接口和抽象类 总结 1. 抽象类和抽象方法 抽象方法和抽象类必须使用abstract修饰符来定义,有抽象方法的类只能被定义成抽象类,抽象类里可以没有抽象方法.抽象方

  • 解析JavaSE的继承和多态

    目录 1.继承 1.子类继承了父类,获得父类的全部Field和方法. 2.子类继承了父类,额外增加新的Field和方法 3.子类继承了父类,重写父类中的方法 4.super限定,在子类调用父类中被覆盖的方法 2.多态 3.引用变量的强制类型转换 4.面试题 1.Java中实现多态的机制是什么? 2.谈谈你对多态的理解? 总结 1. 继承 1. 子类继承了父类,获得父类的全部Field和方法. 子类Student类继承父类,将可以获得父类的全部Field和方法 public class Perso

  • JDK集合源码之解析TreeMap(一)

    目录 简介 继承体系 存储结构 源码解析 属性 Entry内部类 构造方法 get(Object key)方法 特性再回顾 左旋 右旋 插入元素 插入再平衡 插入元素举例 总结 简介 TreeMap使用红黑树存储元素,可以保证元素按key值的大小进行遍历. 继承体系 TreeMap实现了Map.SortedMap.NavigableMap.Cloneable.Serializable等接口. SortedMap规定了元素可以按key的大小来遍历,它定义了一些返回部分map的方法. public

  • 深入解析Java中的内部类

    概述 最近学习python,发现python是支持多继承的,这让我想起Java是通过内部类实现的这套机制.这篇文章不是讲如何通过内部类实现多继承,而是总结一下内部类的类型和使用方法. Java内部类分为: 非静态内部类 静态内部类 局部内部类 匿名内部类 内部类在Android源码中被大量的使用,先介绍一下这四种内部类的共同点: 内部类仍然是一个独立的类,在编译之后内部类会被编译成独立的.class文件,但是前面冠以外部类的类名和$符号. 内部类不能用普通的方式访问.内部类是外部类的一个成员,因

  • 解析ConcurrentHashMap:成员属性、内部类、构造方法分析

    1.简介 ConcurrentHashMap是HashMap的线程安全版本,内部也是使用(数组 + 链表 + 红黑树)的结构来存储元素.相比于同样线程安全的HashTable来说,效率等各方面都有极大地提高. 在学习ConcurrentHashMap源码之前,这里默认大家已经读过HashMap源码,了解LongAdder原子类.红黑树.先简单介绍下 ConcurrentHashMap的整体流程: 整体流程跟HashMap比较类似,大致是以下几步: (1)如果桶数组未初始化,则初始化: (2)如果

  • 解析ConcurrentHashMap:成员属性、内部类、构造方法

    目录 1.简介 2.JDK1.8 ConcurrentHashMap结构图 3.成员属性 4.静态属性 5.静态代码块 6.内部类 6.1 Node节点 6.2 ForwardingNode节点 6.3 TreeNode节点 7.构造方法 8.总结 1.简介 ConcurrentHashMap是HashMap的线程安全版本,内部也是使用(数组 + 链表 + 红黑树)的结构来存储元素.相比于同样线程安全的HashTable来说,效率等各方面都有极大地提高. 在学习ConcurrentHashMap

  • JavaSE static final及abstract修饰符实例解析

    static :静态常量,静态方法,静态代码块 静态变量: 静态变量属于类的,使用类名来访问,非静态变量是属于对象的,"必须"使用对象来访问. 注意:静态变量对于类而言在内存中只有一个,能被类中所有的实例共享. 实例变量对于类的每一个实例都有一份, 它们之间互不影在加载类的过程中为静态变量分配内存,实例变量在创建对象时分配内存 所以静态变量可以使用类名来直接访问,而不需要使用对象来访问. package com.len.collection;public class PersonTes

  • 解析Java中的static关键字

    一.static关键字使用场景 static关键字主要有以下5个使用场景: 1.1.静态变量 把一个变量声明为静态变量通常基于以下三个目的: 作为共享变量使用 减少对象的创建 保留唯一副本 第一种比较容易理解,由于static变量在内存中只会存在一个副本,所以其可以作为共享变量使用,比如要定义一个全局配置.进行全局计数.如: public class CarConstants { // 全局配置,一般全局配置会和final一起配合使用, 作为共享变量 public static final in

  • Java多线程 ThreadLocal原理解析

    目录 1.什么是ThreadLocal变量 2.ThreadLocal实现原理 3.内存泄漏问题 4.使用场景 1)存储用户Session 2)解决线程安全的问题 3)使用ThreadLocal重新设计一个上下文设计模式 4)ThreadLocal注意事项 脏数据 内存泄漏 父子线程共享线程变量 1.什么是ThreadLocal变量 ThreadLoal 变量,线程局部变量,同一个 ThreadLocal 所包含的对象,在不同的 Thread 中有不同的副本. 这里有几点需要注意: 因为每个 T

随机推荐