内部类

内部类是指在一个外部类的内部再定义一个类。内部类的出现,再次打破了Java单继承的局限性。

内部类可以是静态 static 的,也可用 public,default,protected 和 private 修饰。(而外部顶级类即类名和文件名相同的只能使用 public 和 default)。

注意:内部类是一个编译时的概念,一旦编译成功,就会成为完全不同的两个类。

对于一个名为outer的外部类和其内部定义的名为inner的内部类。编译完成后出现 outer.class 和outer$inner.class 两类。所以内部类的成员变量/方法名是可以和外部类的相同的。

1 成员内部类

也就是普通的内部类,它在外部类里面一层。

  • 成员内部类可以访问外部类的所有成员和方法;

  • 外部类要访问成员内部类的成员和方法需要通过实例对象来访问;

  • 成员内部类不能含有 static 的变量和方法

    想一想,非static的内部类,在外部类加载的时候,并不会加载它;可是你这个内部类却拥有 static 的变量和方法,这是必须先加载的,就会和这个内部类的加载时机冲突

    事实上,这种写法编译器就能提示错误:

具体的使用方法,参考如下代码及注释:

public class Outer{
private int outi = 0;//外部类的成员
//外部类的方法
public void outPrint(){
System.out.println("out");
}
//成员内部类
class Inner{
int ini = outi + 1;//内部类可以访问外部类的成员变量
public void inPrint(){
outPrint();//内部类可以访问外部类的成员方法
System.out.println("in");
}
} public static void main(String[] args) {
Outer outer = new Outer();
//这里因为main是静态方法,访问非静态的 Inner对象需要一个Outer对象
Inner inner = outer.new Inner();
inner.inPrint();//外部类访问内部类的成员方法
System.out.println(inner.ini);//外部类访问内部类的成员变量
}
}

2 静态内部类

解决普通的成员内部类不能含有 static 成员的问题,就是将成员内部类声明为 static

可以这么说:静态内部类只能访问其外围类的静态成员,除此之外与非静态内部类没有任何区别。

静态内部类相当于一个逻辑上可以独立出去的类,不过放在了内部而已。

  • 静态内部类不依赖于外部类的加载。(根据是否使用去加载,相当于内外平行的关系。)
  • 静态内部类不能直接访问外部类的非静态成员。(因为外部类加载的时候非静态成员是没有加载的,除非实例化之后)

对比普通成员内部类的代码如下:

public class Outer{
private int outi = 0;//外部类的成员
//外部类的方法
public void outPrint(){
System.out.println("out");
}
//静态成员内部类
static class Inner{
Outer outer = new Outer();
int ini = outer.outi+1;//只能通过实例
//int ini = outi + 1;//不能访问外部类的普通成员变量
public void inPrint(){
outer.outPrint();//只能通过实例
//outPrint();//不能访问外部类的普通成员方法
System.out.println("in");
}
} public static void main(String[] args) {
Inner inner = new Inner();//可以直接new出来,因为相当于是两个独立的类
inner.inPrint();
System.out.println(inner.ini);
}
}

对于静态内部类来说,我们的感受会更加明显,既然两个类都没什么关系了,为什么还要放在内部作为一个内部类呢?

其实就是为了某一些层级关系,当 Inner 的使用范围很小,适用于包含在 Outer 类当中,但又不依赖于外在的类,就这么写,同时还能够让代码的类间层级关系更加清晰,而不用重新放在另一个文件。

3 局部内部类

局部内部类,是指内部类定义在方法内部,或者某一个作用域里的类。

  • 局部内部类只能在方法里面实例化,外面就不行了;
  • 局部内部类访问外部方法的变量,这个变量必须有 final 修饰。

例如下面的代码:

class Outter{
public void outMethod(){
final int i=0;
class Inner{
//使用i
} Inner in=new Inner();
}
}

很明显,和普通的成员内部类是一样的定义,但是这里强调的特殊点:i 为什么必须是 final 的呢?

原因是:

  • 当 JVM 运行到需要创建 Inner 对象之后,Outter 类已经全部运行完毕,那么垃圾回收机制是有可能释放掉局部变量 i 的,而如果按照普通的成员内部类的定义,i 应该可以访问才对,那这个问题需要一个约束,所以编译器解决了这个问题,他会在 Inner 类中创建了一个 i 的备份;
  • 那么就会有新的问题,外部类的 i 变化的时候,内部类的 i 要一致才行;
  • 因此不得不规定死这些局部域必须是常量,必须用 final 修饰,保证数据的一致。

4 匿名内部类

4.1 匿名内部类

当一个局部内部类没有名字,这就是匿名内部类。

  • 实现的方式是建立一个带内容的外部类、或者接口的子类匿名对象
  • 和普通的局部内部类又不同,访问外部方法的变量,这个变量可以不是final修饰。(jdk1.8之后)

比如我们常见的两种

    public static void main(String[] args) {
//第一种方式
new Thread(){
@Override
public void run() {
System.out.println("内部类输出……");
}
}.start();
//第二种方式
new Thread(new Runnable() {
@Override
public void run() {
System.out.println("内部类输出……");
}
}).start();
}
  • 第一种方式就是,建立了一个带内容的外部类,重写了它的方法,但是这个类我们没有起名字,而是直接创建了一个实例;
  • 第二种方式就是通过实现一个接口,实现对应的方法,同样,这个实现类我们也没有起名字,而是直接创建了一个实例。

正是因为上面所说的,匿名内部类可以访问外部的变量,而且不用是 final ,同理也可以持有外部类的引用,这种情况都可能会导致内存泄漏问题。

外部类的生命周期到了,但是却因为内部类持有者外部类的引用,所以导致外部类无法被回收,造成内存泄漏。

解决方案就是:

  • 使用静态内部类,并且不要持有外部类的引用,如果要调用外部类方法或使用外部类属性,可以使用弱引用(前面说过,静态内部类是和外部类平行的,弱引用会安全)。

4.2 匿名内部类和 lambda 表达式

从 java 8 开始,引入了 Lambda 表达式,将代码块作为参数使用更简洁的代码来创建只有一个抽象方法的接口(这种接口被称为函数式接口)的实例

仍然用上面的匿名内部类的代码作为示例:

    public static void main(String[] args) {
new Thread(()-> System.out.println("内部类输出")).start();
}

可以看出,lambda 表达式代替匿名内部类的时候,lambda 代码块写的是代替实现抽象类的方法体,总结一下lambda表达式的语法主要由三部分构成:

  • 形参列表,如果只有一个参数可以省略括号,当无参数类型时可以使用()或者obj来代替。

  • 箭头 ->

  • 代码块部分,如果代码只有一行则可以省略掉花括号,不然使用花括号将lambda表达式的代码部分标记出来。

写法方面,我们再自己定义一个接口,然后写一下 lambda 式有参数的情况:

//自定义接口
interface Origin{
int sum(int a, int b);//待实现方法,有参数
} public class LambdaTest {
public static void main(String[] args) {
//写法1:使用lambda表达式实现接口
Origin o = (int a, int b)-> {
return a+b;
}; //写法2:省略参数类型
Origin o1 = (a, b)->{
return a+b;
}; //写法3,省略花括号(只适用于方法实现只有一行的情况)
Origin o2 = (a, b)-> a+b; System.out.println(o.sum(100,100));
}
}

4.3 java 的四种引用类型

  1. 强引用

    最常见的普通对象引用,只要还有强引用指向一个对象,说明那个对象还活着,垃圾回收不会回收这种对象;

  2. 弱引用

    垃圾回收器一旦发现具有弱引用的对象,不管当前内存空间是否足够,都会回收他的内存。(即使弱引用被其他强引用引用,还是会被回收)

  3. 软引用

    如果一个对象只具备软引用,如果内存空间足够,那么 JVM 就不会 GC 它,如果内存空间不足了,就会GC该对象

  4. 虚引用

    如果一个对象只具有虚引用,那么它就和没有任何引用一样,随时会被JVM当作垃圾进行GC。

不会被回收的情况?

强引用,只要还存在强引用就不会被回收。

关于强引用和弱引用带来的问题, 最明显的就在 ThreadLocal 的使用上。

Java 的各种内部类、Lambda表达式的更多相关文章

  1. Java核心技术-接口、lambda表达式与内部类

    本章将主要介绍: 接口技术:主要用来描述类具有什么功能,而并不给出每个功能的具体实现.一个类可以实现一个或多个接口. lambda表达式:这是一种表示可以在将来的某个时间点执行的代码块的简洁方法. 内 ...

  2. Java函数式编程和lambda表达式

    为什么要使用函数式编程 函数式编程更多时候是一种编程的思维方式,是种方法论.函数式与命令式编程的区别主要在于:函数式编程是告诉代码你要做什么,而命令式编程则是告诉代码要怎么做.说白了,函数式编程是基于 ...

  3. Java基础教程:Lambda表达式

    Java基础教程:Lambda表达式 本文部分内容引用自OneAPM:http://blog.oneapm.com/apm-tech/226.html 引入Lambda Java 是一流的面向对象语言 ...

  4. java函数式编程之lambda表达式

    作为比较老牌的面向对象的编程语言java,在对函数式编程的支持上一直不温不火. 认为面向对象式编程就应该纯粹的面向对象,于是经常看到这样的写法:如果你想写一个方法,那么就必须把它放到一个类里面,然后n ...

  5. 最全最强 Java 8 - 函数编程(lambda表达式)

    Java 8 - 函数编程(lambda表达式) 我们关心的是如何写出好代码,而不是符合函数编程风格的代码. @pdai Java 8 - 函数编程(lambda表达式) 简介 lambda表达式 分 ...

  6. Java 8:掌握 Lambda 表达式

    本文将介绍 Java 8 新增的 Lambda 表达式,包括 Lambda 表达式的常见用法以及方法引用的用法,并对 Lambda 表达式的原理进行分析,最后对 Lambda 表达式的优缺点进行一个总 ...

  7. Java基础进阶:内部类lambda重点摘要,详细讲解成员内部类,局部内部类,匿名内部类,Lambda表达式,Lambda表达式和匿名内部类的区别,附重难点,代码实现源码,课堂笔记,课后扩展及答案

    内部类lambda重点摘要 内部类特点: 内部类可以直接访问外部类,包括私有 外部类访问内部类必须创建对象 创建内部对象格式: 外部类.内部类 对象名=new外部类().new内部类(); 静态内部类 ...

  8. 0028 Java学习笔记-面向对象-Lambda表达式

    匿名内部类与Lambda表达式示例 下面代码来源于:0027 Java学习笔记-面向对象-(非静态.静态.局部.匿名)内部类 package testpack; public class Test1{ ...

  9. Upgrading to Java 8——第一章 Lambda表达式

    第一章 Lambda表达式 Lamada 表达式是Java SE 8中最重要的新特性,长期以来被认为是在Java中缺失的特性,它的出现使整个java 语言变得完整.至少到目前,在这节中你将学习到什么是 ...

  10. Java 终于有 Lambda 表达式啦~Java 8 语言变化——Lambda 表达式和接口类更改【转载】

    原文地址 en cn 下载 Demo Java™ 8 包含一些重要的新的语言功能,为您提供了构建程序的更简单方式.Lambda 表达式 为内联代码块定义一种新语法,其灵活性与匿名内部类一样,但样板文件 ...

随机推荐

  1. 【Leetcode 做题学算法周刊】第八期

    首发于微信公众号<前端成长记>,写于 2020.05.07 背景 本文记录刷题过程中的整个思考过程,以供参考.主要内容涵盖: 题目分析设想 编写代码验证 查阅他人解法 思考总结 目录 15 ...

  2. python3 - 常用的操作数据库

    # 获取手机号数据表的中的数据 sql2 = 'SELECT shoujihao FROM shoujihao' self.cursor.execute(sql2) sjh_dates = self. ...

  3. leetcode刷题记录——数组与矩阵

    @ 目录 283. 移动零 566. 重塑矩阵 485. 最大连续1的个数 240. 搜索二维矩阵 II 378. 有序矩阵中第K小的元素 645. 错误的集合 287. 寻找重复数 667. 优美的 ...

  4. Jmeter 常用函数(5)- 详解 __property

    如果你想查看更多 Jmeter 常用函数可以在这篇文章找找哦 https://www.cnblogs.com/poloyy/p/13291704.html 作用 读取 Jmeter 属性 语法格式 $ ...

  5. 企业级Registry仓库Harbor的部署与简介

    Harbor 是Vmware公司开源的企业级Docker Registry管理项目,开源项目地址:https://github.com/vmware/harbor Harbor的所有组件都在Docke ...

  6. 算法-搜索(3)AVL树

    AVL树高度平衡的二叉搜索树,任一点的平衡印章只能是+1.-1.0,从而尽量降低树的高度. 如果它有n个结点,高度可保持在O(log2n),平均搜索长度也可保持在O(log2n). (1)AVL树的插 ...

  7. 如何使用python移除/删除非空文件夹?

    移除/删除非空文件夹/目录的最有效方法是什么? 1.标准库参考:shutil.rmtree. 根据设计,rmtree在包含只读文件的文件夹树上失败.如果要删除文件夹,不管它是否包含只读文件,请使用 i ...

  8. 实用教程!SPSSAU验证性因子分析思路总结

    验证性因子分析,是用于测量因子与测量项(量表题项)之间的对应关系是否与研究者预测保持一致的一种研究方法.尽管因子分析适合任何学科使用,但以社会科学居多. 目前有很多软件都可以非常便利地实现验证性因子分 ...

  9. Manico--自定义应用快速切换

    快速切换应用的app,使用非常频繁,奈何还是没有钱! 这玩意儿虽然免费,但是时不时跳一个弹框让你购买,也是够烦的,然后我们正好利用逆向工具,对着玩意儿进行破解,让它不再弹框! 下载安装Hopper D ...

  10. Docker 镜像构建之 docker commit

    我们可以通过公共仓库拉取镜像使用,但是,有些时候公共仓库拉取的镜像并不符合我们的需求.尽管已经从繁琐的部署工作中解放出来,但是实际开发时,我们可能希望镜像包含整个项目的完整环境,在其他机器上拉取打包完 ...