浅谈java内部类

前言

内部类,讲完前面的特性,今天就讲下内部类这个用的比较多,出现频率挺高的知识点。

[toc]

what is that?

首先,顾名思义,内部类就是在类的内部,也就是类的类,嵌套在里面的。直接代码介绍,现一般分为成员内部类和局部内部类,还有一种匿名类。内部类拥有对外围对象的引用。大部分使用的都是成员内部类。成员内部类是一种与Field、方法、构造器和初始化块相似的类成员;局部内部类和匿名内部类则不是类成员。

成员内部类

定义在类里面的成员变量域的类,就是成员内部类。此时的内部类作为其外部类的成员,所以可以使用任意访问控制符如private、protected和public等修饰。

1
2
3
4
class A {
//成员内部类,这种包含类的结构
class b{}
}

成员内部类可以分为静态内部类和非静态内部类。注意:根据静态成员不能访问非静态成员的规则,外部类的静态方法、静态代码块不能访问非静态内部类,包括不能使用非静态内部类定义变量、创建实例等。

  • 静态内部类(带static)

static关键字的作用是把类的成员变成类相关,而不是实例相关,即static修饰的成员属于整个类,而不属于单个对象。静态内部类只可以访问静态的外部类的方法和对象。但是静态内部类可以有非静态成员。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

/**
* 这一篇是用来测试静态内部类的使用的
* @author yhy
* @date 1/14
*/
public class StaticInner {
private int num = 7;

/**
* 定义静态常量,这个才可以被静态内部类调用
*/
private static int num2 = 6;
static class Innerclass{
private static int num3;
public void innerfangfa(){
// System.out.println("调用外部类的成员变量:"+num);,这个会报错,因为不能调用非静态的
System.out.println("调用外部类的成员变量:"+num2);
}
}
}

报错信息

外部类使用静态类的代码。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
public class StaticInner {
private int num = 7;

/**
* 定义静态常量,这个才可以被静态内部类调用
*/
private static int num2 = 6;

/**
* 定义一个静态内部类
*/
static class Innerclass{
private static int num3;
private int num4 = 4;
public void innerfangfa(){
// System.out.println("调用外部类的成员变量:"+num);,这个会报错,因为不能调用非静态的
System.out.println("调用外部类的成员变量:"+num2);
}
}

/**
* 这是外部类的方法,用来验证访问内部类的方法
*/
public static void inneracess(){
// 通过类名访问静态内部类的类成员
System.out.println("访问静态内部类:"+Innerclass.num3);
// 通过实例访问静态内部类的实例成员
System.out.println("访问静态内部类:"+new Innerclass().num4);

}
}
  • 非静态内部类(不带static)

内部类可以直接访问外部类的变量、方法等,而外部类只能通过显式创建类才可以。包括可以访问外部类的private。同时,在非静态内部类里不能有静态方法、静态field、静态初始化块。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
/**
* 这一篇是用来讲内部类的一些定义的
* @author yhy
* @date 1/14
*/
public class InnerClass {
private int outernum = 9;

/**
* 创建内部类,同时还定义一个常量,为private
*/
class Inner{
private int innernum = 8;
// 定义内部类的方法,去调用外部类的成员常量
public void outAcess(){
System.out.println("外部类的值:"+outernum);
}
}

/**
* 这个是外部类的成员方法
* 这里直接调用内部类会报错,不可以直接innernum使用
* 要通过显式创建内部类对象才可以实现
*/
public void innerAcess(){
System.out.println("内部类的值:"+new Inner().innernum);

}
public static void main(String[] args) {
// 创建外部类对象,没创建内部类对象
InnerClass i = new InnerClass();
i.innerAcess();

}

}
//非静态内部类对象必须寄存在外部类对象里,而外部类对象则不必一定有非静态内部类对象寄存其中

非静态内部类的方法内访问某个变量时,系统优先在该方法内查找(如果存在就使用)——if not,则到该方法所在的内部类中(存在则使用)——if not,则到该内部类所在的外部类(如果存在则使用)——if not,系统将出现编译错误。

局部内部类

局部内部类是定义在类的内部方法或者其他作用域里面的内部类。

1
2
3
4
5
6
class A {
//局部内部类
public void B(){
class B{}
}
}

局部内部类的使用

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
package music.daima.ebook;
/**
* * 这一篇是用来观察局部内部类的定义使用等,thinking in java的练习九
* * @author yhy
* * @date 1/14
* */

interface Ex9Interface{
/**
* @param s string类型在下面给内部类调用
* 定义一个接口,同时包含一个say方法,给下面使用
*/
void say(String s);
}
public class JuBuInner {
/**
* @return Inner()返回该类对此接口的引用
* // 这是一个方法,下面在里面定义一个内部类
*/

Ex9Interface f() {
class Inner implements Ex9Interface {
@Override
public void say(String s) {
System.out.println(s);
}
}
return new Inner();
}
public static void main(String[] args) {
JuBuInner x = new JuBuInner();
// 调用局部内部类的say方法
x.f().say("hi");
}
}
  • 局部内部类不可以为publicprivate来修饰。而且因为它的上一级是方法,所以用static来修饰它是没有意义的,所以也不能用static来修饰

匿名内部类

这个知识点就直接看代码了解一下,次数少的时候可以用它,因为用完就是垃圾了,是内部类的简化写版 ,本质不是类而是匿名对象(继承该类或者实现了改接口的)。

前提

得提前存在一个类或者是接口给它用,可以是具体类也可以是抽象类。

格式

new 类名or接口名() {重写方法};

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
package music.daima.ebook;
/**
* thinking in java
* @author yhy
* 匿名内部类
*/
public class AnonymousInner {
public static test1 getTest(int i){
// 匿名类的结构如下,这里的i会传给基类的构造器
return new test1(i) {
{
System.out.println(22);
}
// 重写f方法
@Override
public void f() {
System.out.println("在匿名类内");
}
};//这里要注意有分号
}
public static void main(String[] args) {
test1 abc = getTest(55);
abc.f();
}
}

/**
*创建一个抽象类
*/
abstract class test1{
// 基类的构造器
public test1(int i){
System.out.println("i is :"+i);
}
public abstract void f();
}

《Java的编程思想》中的练习题答案——使用匿名内部类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
interface Inner12 {
void modifyOuter();
}

public class Outer12 {
private int oi = 1;
private void hi() { System.out.println("Outer hi"); }
public Inner12 inner() {
return new Inner12() {
public void modifyOuter() {
oi *= 2;
hi();
}
};
}
public void showOi() { System.out.println(oi); }
public static void main(String[] args) {
Outer12 out = new Outer12();
out.showOi();
out.inner().modifyOuter();
out.showOi();
}
}

下面的pd.method()方法调用结果是一样的,下面用的就是匿名内部类,相对简单一点,但是不好理解。记住它是一个对象而不是类。

why use it?

  1. 内部类方法可以访问该类定义所在的作用域中的数据,包括私有的数据

  2. 内部类可以对同一个包内的其他类隐藏起来

  3. 当想要定义一个回调函数且不想编写大量代码,可以考虑使用匿名内部类比较便捷

    ​ ——《Java核心技术卷一》

个人理解:

  • 内部类可以将其封装起来,很好的隐藏,可以用protected 和private权限来修饰。
  • 拥有访问外部类的所有元素的权限,普通的内部类对象隐式地保存了一个引用,指向创建它的外围类对象。
  • 比如可以很好处理类的办法同名重叠情况,更加有效地去引用。
  • 可以间接实现了多重继承,内部类继承其它类,然后实例化外部类可以实现这种间接效果

how to use?

1.在外部类以外使用静态内部类

(因为静态内部类是外部类类相关的,因此创建内部类对象时无须创建外部类对象。)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25

/**这是用来测试在外部类以外使用静态内部类
* @author yhy
*/
public class innerOut {
public static void main(String[] args) {
Out1.In1 abc = new Out1.In1();
}
}

/**
* 定义一个外部类
* @author yhy
*/
class Out1{
/**
* 定义一个静态内部类,静态内部类是直接与外部类相关的
*/
static class In1{
void FangFa(){
System.out.println("这是静态内部类");
}
}
}
//output:这是静态内部类

2.在外部类以外使用非静态内部类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27

/**
* 使用教程——外部类以外使用非静态内部类
* @author yhy
*/
public class InnerTest {
public static void main(String[] args) {
// 格式,先外部再内部 = new 外部.new 内部
Out.In test = new Out().new In();
test.inner();

}
}
/**
* 定义一个外部类
*/
class Out{
/**
* 定义一个非静态内部类
*/
class In{
void inner(){
System.out.println("打印出内部类的信息");
}
}
}
//output:打印出内部类的信息

3.在外部类内部使用内部类

在上面有使用的代码

总结

静态内部类和非静态内部类区别只是在创建内部类对象时,静态的只需使用外部类即可调用构造器,而非静态则一定使用外部类对象来调用里面的方法。

浅谈垃圾回收

前言

Java的垃圾回收是Java语言的重要功能之一。当程序创建对象、数组等引用类型实体时,系统都会在堆内存中为之分配一块内存区,对象就保存在这块内存区中,当这块内存不再被任何引用变量引用时,这块内存就变成垃圾,等待垃圾回收机制进行回收。垃圾回收机制具有如下特征。

finalize()方法

终结处理和垃圾回收。

Java允许在类中定义一个finalize()的方法:一旦垃圾回收器准备好释放对象占用的存储空间,将首先调用其finalize(),并在下一次垃圾回收动作发生时,才会真正回收对象占用的内存。当用上finalize方法时候,就能在垃圾回收时刻做一些必要的工作。

image-20191231011154761.png

浅谈java继承

[TOC]

前言

类的继承性是面向对象语言的基本特性,多态性前提是继承性。Java 支持继承性和多态性。——Java从小白到大牛

继承:三大特性之一,关键词:extends

简单用伪代码去介绍一些。

  • 假设需要定义个student类
1
2
3
4
5
public class Student{
private String name;//名字
private int age;//年龄
private int sex;//性别
}
  • 再来个需求,需要定义一个学生Andy,不用继承时候,需要一个个去定义;
1
2
3
4
5
6
public class Andy{
private String name;
private int age;
private int sex;
private String country;//新增一个国家信息
}
  • 从上面看两者有着很多的相似的重复定义的东西,如果使用继承去写这段代码就可以如下:
1
2
3
public class Andy extends student{
private String country
}

所以从上面可以看出类的继承大致用途,Andy类已经继承了Student类,上面的父类便是student,extends后面跟着的。继承的时候,要明确指出,要不然就是在Java标准根类Object进行继承。

再者,在继承的过程中,除了你可以在新的类中去添加东西,也不一定非得使用基类的方法属性等。

  • 还可以有多层继承关系(下面的输出是随便定义的伪代码,可以是直接输出某一些语句,方便运行查看)
1
2
3
4
5
6
7
8
9
10
11
12
13
class Art{
Art(){输出1}
}
class Music extends Art{
Music(输出2)
}
class Dance extends music{
public Dance(){输出3}
public static void main(String args[]){
dance x = new dance();
}
}
//输出的顺序是1 2 3

父类与子类的构造函数问题

  • 若不在类中定义构造函数,会默认生成一个无参的函数构造器,如果有了就不会。(构造器即构造函数,与类同名,在初始化的时候就被调用,,默认的构造函数是不带任何参数的)
1
2
3
4
5
6
7
8
9
class Art


//等同于下面
class Art
public Art(){
//
}

  • ​ super关键字

Java用super表示超类的医生,当前类是从超类继承来的。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
class Game{
int h;
Game(int i){//构造函数
System.out.println("this is a game!");
}
public static void printg(int d){//普通函数
System.out.println("12");
}
}
class ballGame extends Game{

ballGame(int i){
super(i);//调用父类的构造器,漏了这句就会报错,显示there is no default constructor available
System.out.println("ballGame is coming!");
super.h = 3;//访问父类的属性
}
}

super可以用于访问父类定义的属性,用于调用父类中的成员方法,super代表父类的内存空间。可以访问父类的父类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
class Game{
int h;
Game(int i){//构造函数
System.out.println(i);//输出
System.out.println("第一层");
}
public static void printg(int d){//普通函数
System.out.println("1");
}
}
class ballGame extends Game{
ballGame(int i){
super(i);//调用父类的构造器
System.out.println("第二层");
super.h = 2;//访问父类的属性
}
}

public class jichengSuper extends ballGame{
jichengSuper(int b) {
super(b);//调用的是上面父类的构造函数,将输出3
int a = super.h ;//将第二层里的3赋值给a
System.out.println(a);//打印
}
public static void main(String args[]){
new jichengSuper(3);//这里的3将代进去上面的构造函数jichengSuper中
System.out.print("最后一层");
}

}
/**
*output:
3
第一层
第二层
2
最后一层
*/
  • 接口在实现的时候,在类中一定要实现接口的抽象方法

  • 而继承不用,想要用哪个再用

others

可以有多个接口但是只能有一个继承。一般情况下,一个子类只能继承一个父类,这称为“单继承”,但有的情况下一个子类可以有多个不同的父类,这称为“多重继承”。在Java中,类的继承只能是单继承,而多重继承可以通过实现多个接口实现。