面向对象三

面向对象三

关键字:static

static:静态的

类属性、类方法的设计思想

  • 类属性作为该类各个对象之间共享的变量。在设计类时,分析哪些属性不因对象的不同而改变,将这些属性设置为类属性。相应的方法设置为类方法
  • 如果方法与调用者无关,则这样的方法通常被声明为类方法,由于不需要创建对象就可以调用类方法,从而简化了方法的调用

可以用来修饰的结构

主要用来修饰类的内部结构,属性、方法、代码块、内部类

static修饰属性

静态变量(或类变量)

属性

是否使用static修饰,又分为:静态属性 vs 非静态属性(实例变量)

  • 实例变量:我们创建了类的多个对象,每个对象都独立的拥一套类中的非静态属性。当修改其中一个对象中的非静态属性时,不会导致其他对象中同样的属性值的修改。

  • 静态变量:我们创建了类的多个对象,多个对象共享同一个静态变量。当通过某一个对象修改静态变量时,会导致其他对象调用此静态变量时,是修改过了的。

static修饰属性的其他说明

① 静态变量随着类的加载而加载。可以通过”类.静态变量”的方式进行调用

② 静态变量的加载要早于对象的创建。

③ 由于类只会加载一次,则静态变量在内存中也只会存在一份,存在方法区的静态域中。

类变量 实例变量
yes no
对象 yes yes

静态属性举例

System.out;

Math.PI;

静态变量内存解析:

img

static的注意点:

在静态的方法内,不能使用this关键字、super关键字

static修饰的方法不能被重写

关于静态属性和静态方法的使用,大家都从生命周期的角度去理解

如何判定属性和方法应该使用static关键字

关于属性

属性是可以被多个对象所共享的,不会随着对象的不同而不同的。

类中的常量也常常声明为static

关于方法

操作静态属性的方法,通常设置为static的

工具类中的方法,习惯上声明为static的。 比如:Math、Arrays、Collections

没有对象的实例时,可以用类名.方法名()的形式访问由static修饰的类方法

在static方法内部只能访问类的static修饰的属性或方法,不能访问类的非static的结构

使用举例

举例一:Arrays、Math、Collections等工具类

举例二:单例模式

举例三:

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
39
40
41
42
class Circle{

private double radius;
private int id;//自动赋值

public Circle(){
id = init++;
total++;
}

public Circle(double radius){
this();
//id = init++;
//total++;
this.radius = radius;

}

private static int total;//记录创建的圆的个数
private static int init = 1001;//static声明的属性被所对象所共享

public double findArea(){
return 3.14 * radius * radius;
}

public double getRadius() {
return radius;
}

public void setRadius(double radius) {
this.radius = radius;
}

public int getId() {
return id;
}

public static int getTotal() {
return total;
}

}

静态方法不能被重写

单例模式

设计模式的说明

设计模式是在大量的实践中总结和理论化之后优的代码结构、编程风格、以及解决问题的思考方式。

常用设计模式 — 23种经典的设计模式 GOF

创建型模式,共5种:工厂方法模式、抽象工厂模式、单例模式、建造者模式、原型模式。

结构型模式,共7种:适配器模式、装饰器模式、代理模式、外观模式、桥接模式、组合模式、享元模式。

行为型模式,共11种:策略模式、模板方法模式、观察者模式、迭代器模式、责任链模式、命令模式、备忘录模式、状态模式、访问者模式、中介者模式、解释器模式。

单例模式

要解决的问题

所谓类的单例设计模式,就是采取一定的方法保证在整个的软件系统中,对某个类只能存在一个对象实例。

具体代码的实现

饿汉式1:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
class Bank{

//1.私化类的构造器
private Bank(){

}

//2.内部创建类的对象
//4.要求此对象也必须声明为静态的
private static Bank instance = new Bank();

//3.提供公共的静态的方法,返回类的对象
public static Bank getInstance(){
return instance;
}
}

饿汉式2:使用了静态代码块

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

//1.私化类的构造器
private Order(){

}

//2.声明当前类对象,没初始化
//4.此对象也必须声明为static的
private static Order instance = null;

static{
instance = new Order();
}

//3.声明public、static的返回当前类对象的方法
public static Order getInstance(){
return instance;
}

}

懒汉式:

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

//1.私化类的构造器
private Order(){

}

//2.声明当前类对象,没初始化
//4.此对象也必须声明为static的
private static Order instance = null;

//3.声明public、static的返回当前类对象的方法
public static Order getInstance(){

if(instance == null){

instance = new Order();

}
return instance;
}

}

2.3 两种方式的对比:

饿汉式:

坏处:对象加载时间过长。

好处:饿汉式是线程安全的 

懒汉式:好处:延迟对象的创建。

​ 目前的写法坏处:线程不安全—>到多线程内容时,再修改

main()的使用说明

main()方法作为程序的入口

main()方法也是一个普通的静态方法

main()方法可以作为我们与控制台交互的方式。(之前:使用Scanner)

由于Java虚拟机需要调用类的main()方法,所以该方法的访问权限必须是public,又因为Java虚拟机在执行main()方法时不必创建对象,所以该方法必须是static的,该方法接收一个String类型的数组参数,该数组中保存执行Java命令时传递给所运行的类的参数

又因为main() 方法是静态的,我们不能直接访问该类中的非静态成员,必须创建该类的一个实例对象后,才能通过这个对象去访问类中的非静态成员,这种情况,我们在之前的例子中多次碰到

如何将控制台获取的数据传给形参

使用String[] args

1
2
3
4
5
6
7
public class MainTest {
public static void main(String[] args) {
for (int i=0;i<args.length;i++){
System.out.println("****"+args[i]);
}
}
}

image-20200206145555121

注意:arguments中的数据加不加双引号都可以 ,默认获取的都是String,如果是其他类型,需要将String类型转换为该类型

输出结果

1
2
3
****刘德华
****张学友
****张小于

小结

1
2
3
public static void main(String[] args){
//方法体
}

权限修饰符:private 缺省 protected pubilc —->封装性

修饰符:static \ final \ abstract \native 可以用来修饰方法

返回值类型: 无返回值 / 有返回值 –>return

方法名:需要满足标识符命名的规则、规范;”见名知意”

形参列表:重载 vs 重写,参数的值传递机制,体现对象的多态性

方法体:来体现方法的功能

类的结构:代码块

类的成员之四:代码块(初始化块)(重要性较属性、方法、构造器差一些)

代码块的作用

用来初始化类、对象的信息

分类

代码块要是使用修饰符,只能使用static

分类:静态代码块 vs 非静态代码块

静态代码块

内部可以输出语句

随着类的加载而执行,而且只执行一次

作用:初始化类的信息

如果一个类中定义了多个静态代码块,则按照声明的先后顺序执行

静态代码块的执行要优先于非静态代码块的执行

静态代码块内只能调用静态的属性、静态的方法,不能调用非静态的结构

非静态代码块

内部可以输出语句

随着对象的创建而执行

每创建一个对象,就执行一次非静态代码块

作用:可以在创建对象时,对对象的属性等进行初始化

如果一个类中定义了多个非静态代码块,则按照声明的先后顺序执行

非静态代码块内可以调用静态的属性、静态的方法,或非静态的属性、非静态的方法

实例化对象加载顺序

实例化子类对象时,涉及到父类、子类中静态代码块、非静态代码块、构造器的加载顺序:

对应的练习:LeafTest.java / Son.java

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
39
40
41
42
43
44
45
46
47
48
49
50
class Root{
static{
System.out.println("Root的静态初始化块");
}
{
System.out.println("Root的普通初始化块");
}
public Root(){
super();
System.out.println("Root的无参数的构造器");
}
}
class Mid extends Root{
static{
System.out.println("Mid的静态初始化块");
}
{
System.out.println("Mid的普通初始化块");
}
public Mid(){
super();
System.out.println("Mid的无参数的构造器");
}
public Mid(String msg){
//通过this调用同一类中重载的构造器
this();
System.out.println("Mid的带参数构造器,其参数值:"
+ msg);
}
}
class Leaf extends Mid{
static{
System.out.println("Leaf的静态初始化块");
}
{
System.out.println("Leaf的普通初始化块");
}
public Leaf(){
//通过super调用父类中有一个字符串参数的构造器
super("尚硅谷");
System.out.println("Leaf的构造器");
}
}
public class LeafTest{
public static void main(String[] args){
new Leaf();
System.out.println("******");
new Leaf();
}
}

输出结果

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
Root的静态初始化块
Mid的静态初始化块
Leaf的静态初始化块
Root的普通初始化块
Root的无参数的构造器
Mid的普通初始化块
Mid的无参数的构造器
Mid的带参数构造器,其参数值:尚硅谷
Leaf的普通初始化块
Leaf的构造器
******
Root的普通初始化块
Root的无参数的构造器
Mid的普通初始化块
Mid的无参数的构造器
Mid的带参数构造器,其参数值:尚硅谷
Leaf的普通初始化块
Leaf的构造器

由父及子,静态先行

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
39
class Father {
static {
System.out.println("11111111111");
}
{
System.out.println("22222222222");
}

public Father() {
System.out.println("33333333333");

}

}

public class Son extends Father {
static {
System.out.println("44444444444");
}
{
System.out.println("55555555555");
}
public Son() {
System.out.println("66666666666");
}


public static void main(String[] args) { // 由父及子 静态先行
System.out.println("77777777777");
System.out.println("************************");
new Son();
System.out.println("************************");

new Son();
System.out.println("************************");
new Father();
}

}

输出结果

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
11111111111
44444444444
77777777777
************************
22222222222
33333333333
55555555555
66666666666
************************
22222222222
33333333333
55555555555
66666666666
************************
22222222222
33333333333

属性的赋值顺序

①默认初始化

②显式初始化/⑤在代码块中赋值

显示初始化与代码块赋值属于同级别,按先后顺序执行

③构造器中初始化

④有了对象以后,可以通过”对象.属性”或”对象.方法”的方式,进行赋值

执行的先后顺序:① - ② / ⑤ - ③ - ④

image-20200206151439968

关键字:final

final:最终的

可以用来修饰

类、方法、变量

具体细节

  • final 用来修饰一个类,此类不能被其他类所继承

    比如:String类、System类、StringBuffer类

  • final 用来修饰方法,表明此方法不可以被重写

    比如:Object类中getClass();

  • final 用来修饰变量:此时的”变量”就称为是一个常量

​ 1. final修饰属性:可以考虑赋值的位置:显式初始化、代码块中初始化、构造器中初始化

​ 2. final修饰局部变量:尤其是使用final修饰形参时,表明此形参是一个常量。当我们调用此方法时,给常量形参赋一个实参。一旦赋值以后,就只能在方法体内使用此形参,但不能进行重新赋值。

static final 用来修饰属性:全局常量

关键字:abstract

随着继承层次中一个个新子类的定义,类变得越来越具体,而父类则更一般,更通用。类的设计应该保证父类和子类能够共享特征。有时将一个父类设计得非常抽象,以至于它没有具体的实例,这样的类叫做抽象类

abstract

抽象的,可以用来修饰:类、方法

abstract修饰类

抽象类

  • 此类不能实例化

  • 抽象类中一定有构造器,便于子类实例化时调用(涉及:子类对象实例化的全过程)

  • 开发中,都会提供抽象类的子类,让子类对象实例化,完成相关的操作 —>抽象的使用前提:继承性

  • 若没有重写全部的抽象方法,仍为抽象类

abstract修饰方法

抽象方法

  • 抽象方法只方法的声明,没方法体

  • 包含抽象方法的类,一定是一个抽象类。反之,抽象类中可以没有抽象方法的。

  • 若子类重写了父类中的所的抽象方法后,此子类方可实例化

  • 若子类没重写父类中的所的抽象方法,则此子类也是一个抽象类,需要使用abstract修饰

注意点

  • abstract不能用来修饰:属性、构造器等结构

  • abstract不能用来修饰私有方法、静态方法、final的方法、final的类

abstract的应用举例

举例一:

img

举例二:

1
2
3
4
5
6
7
8
9
10
11
12
public abstract class GeometricObject{
public abstract double findArea();
}

class Circle extends GeometricObject{

private double radius;
@Override
public double findArea() {
return 0;
}
}

举例三:

IO流中设计到的抽象类,即在InputStream/OutputStream / Reader /Writer内部定义了抽象的read()、write()方法

模板方法的设计模式

解决的问题

在软件开发中实现一个算法时,整体步骤很固定、通用,这些步骤已经在父类中写好了。但是某些部分易变,易变

部分可以抽象出来,供不同子类实现。这就是一种模板模式。

举例

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
39
40
41
abstract class Template {

//计算某段代码执行所需要花费的时间
public void spendTime() {

long start = System.currentTimeMillis();

this.code();//不确定的部分、易变的部分

long end = System.currentTimeMillis();

System.out.println("花费的时间为:" + (end - start));

}

public abstract void code();

}

class SubTemplate extends Template {

@Override
public void code() {

for (int i = 2; i <= 1000; i++) {
boolean isFlag = true;
for (int j = 2; j <= Math.sqrt(i); j++) {

if (i % j == 0) {
isFlag = false;
break;
}
}
if (isFlag) {
System.out.println(i);
}
}

}

}

应用场景

img

关键字:interface

interface:接口

接口使抽象方法和常量值定义的集合

用interface来定义

接口中的所有成员变量都默认是由public static final修饰的

接口中的所有抽象方法都默认是由public abstract修饰的

接口中没有构造器

接口采用多继承机制

image-20200206184440998

使用说明

接口使用interface来定义

Java中,接口和类是并列的两个结构

接口中不能定义构造器的,意味着接口不可以实例化

Java开发中,接口通过让类去实现implements的方式来使用

如果实现类覆盖了接口中的所抽象方法,则此实现类就可以实例化,如果实现类没覆盖接口中所的抽象方法,则此实现类仍为一个抽象类

Java类可以实现多个接口 —>弥补了Java单继承性的局限性,格式:class AA extends BB implements CC,DD,EE

接口与接口之间可以继承,而且可以多继承

接口的具体使用,体现多态性,与继承关系类似,接口与实现类之间存在多态性

接口,实际上可以看做是一种规范

如何定义接口

定义接口中的成员

JDK7及以前

只能定义全局常量和抽象方法

  • 全局常量:public static final的,但是书写时,可以省略不写

  • 抽象方法:public abstract

JDK8

除了定义全局常量和抽象方法之外,还可以定义静态方法、默认方法

举例

img

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
39
40
41
42
43
44
45
46
47
48
class Computer{

public void transferData(USB usb){//USB usb = new Flash();
usb.start();

System.out.println("具体传输数据的细节");

usb.stop();
}


}

interface USB{
//常量:定义了长、宽、最大最小的传输速度等

void start();

void stop();

}

class Flash implements USB{

@Override
public void start() {
System.out.println("U盘开启工作");
}

@Override
public void stop() {
System.out.println("U盘结束工作");
}

}

class Printer implements USB{
@Override
public void start() {
System.out.println("打印机开启工作");
}

@Override
public void stop() {
System.out.println("打印机结束工作");
}

}

体会:

  1. 接口使用上也满足多态性
  2. 接口,实际上就是定义了一种规范
  3. 开发中,体会面向接口编程

练习

  • 定义一个接口用来实现两个对象的比较

    interface CompareObject{
    public int compareTo(Object o); //若返回值是 0 , 代表相等; 若为正数,代表当前对象大;负数代表当前对    象小
    }
  • 定义一个Circle类,声明redius属性,提供getter和setter方法

  • 定义一个ComparableCircle类,继承Circle类并且实现CompareObject接口。在ComparableCircle类中给出接口中方法compareTo的实现体,用来比较两个圆的半径大小

  • 定义一个测试类InterfaceTest,创建两个ComparableCircle对象,调用compareTo方法比较两个类的半径大小

  • 思 考 : 参 照 上 述 做 法 定 义 矩 形 类 Rectangle 和 ComparableRectangle 类 , 在ComparableRectangle类中给出compareTo方法的实现,比较两个矩形的面积大小

CompareObject接口

1
2
3
4
5
6
7
8
9
10
/*
interface CompareObject{
public int compareTo(Object o);
//若返回值是 0 , 代表相等; 若为正数,代表当前对象大;负数代表当前对象小
}
*/
public interface CompareObject {
//若返回值是 0 , 代表相等; 若为正数,代表当前对象大;负数代表当前对象小
public int compareTo(Object o);
}

Circle类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
/*
定义一个Circle类,声明redius属性,提供getter和setter方法
*/
public class Circle {

private Double radius;

public Circle() {
super();
}

public Circle(Double radius) {
this.radius = radius;
}

public Double getRadius(){
return radius;
}

public void setRadius(Double radius){
this.radius=radius;
}

}

ComparableCircle类

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
public class ComparableCircle extends Circle implements CompareObject {

public ComparableCircle(double radius){
super(radius);
}
@Override
public int compareTo(Object o) {
if(this==o){
return 0;
}
if(o instanceof ComparableCircle){
ComparableCircle c=(ComparableCircle)o;
/*if (this.getRadius()>c.getRadius()) {
return 1;
}else if(this.getRadius()<c.getRadius()){
return -1;
}else {
return 0;
}*/
return this.getRadius().compareTo(c.getRadius());
}else{
return 0;
}
}
}

InterfaceTest测试类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class ComparebleCricleTest {
public static void main(String[] args) {
ComparableCircle c1 = new ComparableCircle(3.4);
ComparableCircle c2 = new ComparableCircle(3.6);

int compareValue=c1.compareTo(c2);
if(compareValue>0){
System.out.println("c1对象大");
}else if(compareValue<0){
System.out.println("c2对象大");
}else {
System.out.println("c1与c2一样大");
}
}
}

体会面向接口编程的思想

img

面向接口编程:我们在应用程序中,调用的结构都是JDBC中定义的接口,不会出现具体某一个数据库厂商的API

Java8中关于接口的新规范

Java 8中,你可以为接口添加静态方法和默认方法。从技术角度来说,这是完全合法的,只是它看起来违反了接口作为一个抽象定义的理念

静态方法:使用 static 关键字修饰。可以通过接口直接调用静态方法,并执行其方法体。我们经常在相互一起使用的类中使用静态方法。你可以在标准库中找到像Collection/Collections或者Path/Paths这样成对的接口和类

默认方法:默认方法使用 default 关键字修饰。可以通过实现类对象来调用。我们在已有的接口中提供新方法的同时,还保持了与旧版本代码的兼容性。比如:java 8 API中对Collection、List、Comparator等接口提供了丰富的默认方法

AA接口

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public interface AA {
double PI = 3.14;
//静态方法
public static void method1() {
System.out.println("hello lambda!");
}
//默认方法
public default void method2() {
System.out.println("北京");
}
default String method3() {
return "上海";
}
}

CompareB接口

1
2
3
4
5
public interface CompareB {
default String method3(){
return "CompareB,上海";
}
}

SuperClass类

1
2
3
4
5
6
7
public class SuperClass {

public void method(){
System.out.println("SuperClass:北京");

}
}

测试类

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
public class SubClassTest {
public static void main(String[] args) {
SubClass s=new SubClass();
//知识点1:接口中定义的静态方法,只能通过接口来调用
AA.method1();
//知识点2:通过实现类的对象,可以调用接口中的默认方法
//如果实现类重写了接口中的默认方法,调用时,仍然调用的是重写以后的方法
s.method2();
//如果实现类实现了多个接口,而这多个接口中定义了同名同参数的默认方法,那么在实现类没重写此方法的情况下,报错。-->接口冲突。
//这就需要我们必须在实现类中重写此方法
s.method3();
}
}
class SubClass extends SuperClass implements AA,CompareB{
@Override
public void method2(){
System.out.println("SubClass,上海");
}

@Override
public String method3() {
return "";
}

//在子类(或实现类)的方法中调用父类、接口中被重写的方法
public void myMethod(){
//调用自己定义的重写的方法
method3();
//调用的是父类中的声明的方法
super.method();
//调用接口中的默认方法
AA.super.method2();
CompareB.super.method3();

}
}

面试题

抽象类和接口的异同?

相同点:

不能实例化,都可以包含抽象方法的

不同点:

1)把抽象类和接口(java7,java8,java9)的定义、内部结构解释说明

2)类:单继承性 接口:多继承

类与接口:多实现

image-20200206184714593

面试题

1
2
3
4
5
6
7
8
9
10
11
12
13
14
interface A {
int x = 0;
}
class B {
int x = 1;
}
class C extends B implements A {
public void pX() {
System.out.println(x);
}
public static void main(String[] args) {
new C().pX();
}
}

排错

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
interface A {
int x = 0;
}
class B {
int x = 1;
}
class C extends B implements A {
public void pX() {
//编辑不通过,此时的x不确定是A的x,还是B的x
//System.out.println(x);
System.out.println(super.x);
System.out.println(A.x);

}
public static void main(String[] args) {
new C().pX();
}
}

面试题

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
interface Playable {
void play();
}

interface Bounceable {
void play();
}

interface Rollable extends Playable, Bounceable {
Ball ball = new Ball("PingPang");
}

public class Ball implements Rollable {
private String name;
public String getName() {
return name;
}
public Ball(String name) {
this.name = name;
}
public void play() {
//编译不通过
//Ball ball = new Ball("PingPang")全称为public static finall Ball ball = new Ball("PingPang"),因为是finall类型所以不能创建对象
ball = new Ball("Football");
System.out.println(ball.getName());
}
}

代理模式

解决的问题

代理模式是Java开发中使用较多的一种设计模式。代理设计就是为其他对象提供一种代理以控制对这个对象的访问

举例

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
interface NetWork{

public void browse();

}

//被代理类
class Server implements NetWork{

@Override
public void browse() {
System.out.println("真实的服务器访问网络");
}

}
//代理类
class ProxyServer implements NetWork{

private NetWork work;

public ProxyServer(NetWork work){
this.work = work;
}


public void check(){
System.out.println("联网之前的检查工作");
}

@Override
public void browse() {
check();

work.browse();

}

}

应用场景

img

工厂的设计模式

解决的问题

实现了创建者与调用者的分离,即将创建对象的具体过程屏蔽隔离起来,达到提高灵活性的目的

具体模式

简单工厂模式:用来生产同一等级结构中的任意产品。(对于增加新的产品,需要修改已有代码)

工厂方法模式:用来生产同一等级结构中的固定产品。(支持增加任意产品)

抽象工厂模式:用来生产不同产品族的全部产品。(对于增加新的产品,无能为力;支持增加产品族)

类的结构:内部类

内部类:类的第五个成员

定义

Java中允许将一个类A声明在另一个类B中,则类A就是内部类,类B称为外部类

Inner class一般用在定义它的类或语句块之内,在外部引用它时必须给出完整的名称

Inner class的名字不能与包含它的外部类类名相同

内部类的分类

成员内部类(静态内部类、非静态内部类 )

局部内部类(方法内、代码块内、构造器内)

成员内部类的理解

1作为外部类的成员

调用外部类的结构

可以被static修饰,但此时就不能再使用外部类的非static的成员变量

可以被4种不同的权限修饰

2作为一个类

类内可以定义属性、方法、构造器等

可以被final修饰,表示此类不能被继承。言外之意,不使用final,就可以被继承

可以被abstract修饰

编译以后生成OuterClass$InnerClass.class字节码文件(也适用于局部内部类)

注意

  1. 非static的成员内部类中的成员不能声明为static的,只有在外部类或static的成员
    内部类中才可声明static成员。

  2. 外部类访问成员内部类的成员,需要“内部类.成员”或“内部类对象.成员”的方式

  3. 成员内部类可以直接使用外部类的所有成员,包括私有的数据

  4. 当想要在外部类的静态成员部分使用内部类时,可以考虑内部类声明为静态的

如何创建成员内部类的对象

(静态的,非静态的)

1
2
3
4
5
6
7
8
9
//创建静态的Dog内部类的实例(静态的成员内部类):

Person.Dog dog = new Person.Dog();

//创建非静态的Bird内部类的实例(非静态的成员内部类):

//Person.Bird bird = new Person.Bird();//错误的
Person p = new Person();
Person.Bird bird = p.new Bird();

如何在成员内部类中调用外部类的结构

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
class Person{
String name = "小明";
public void eat(){
}
//非静态成员内部类
class Bird{
String name = "杜鹃";
public void display(String name){
System.out.println(name);//方法的形参
System.out.println(this.name);//内部类的属性
System.out.println(Person.this.name);//外部类的属性
//Person.this.eat();
}
}
}

局部内部类的使用

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
//返回一个实现了Comparable接口的类的对象
public Comparable getComparable(){

//创建一个实现了Comparable接口的类:局部内部类
//方式一:
// class MyComparable implements Comparable{
//
// @Override
// public int compareTo(Object o) {
// return 0;
// }
//
// }
//
// return new MyComparable();

//方式二:
return new Comparable(){

@Override
public int compareTo(Object o) {
return 0;
}

};

}

注意点:

在局部内部类的方法中(比如:show如果调用局部内部类所声明的方法(比如:method)中的局部变量(比如:num)的话,要求此局部变量声明为final的。

jdk 7及之前版本:要求此局部变量显式的声明为final的

jdk 8及之后的版本:可以省略final的声明

总结:

成员内部类和局部内部类,在编译以后,都会生成字节码文件。

格式:成员内部类:外部类$内部类名.class

​ 局部内部类:外部类$数字 内部类名.class

image-20200207035451888 image-20200207035518692

匿名内部类

image-20200207035714396
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
interface A {
public abstract void fun1();
}

public class Outer {
public static void main(String[] args) {
new Outer().callInner(new A() {
//接口是不能new但此处比较特殊是子类对象实现接口,只不过没有为对象取名

@Override
public void fun1() {
System.out.println("implement for fun1");
}
});// 两步写成一步了
}

public void callInner(A a) {
a.fun1();
}
}
文章目录
  1. 1. 面向对象三
    1. 1.1. 关键字:static
      1. 1.1.1. 类属性、类方法的设计思想
      2. 1.1.2. 可以用来修饰的结构
      3. 1.1.3. static修饰属性
        1. 1.1.3.1. 属性
        2. 1.1.3.2. static修饰属性的其他说明
        3. 1.1.3.3. 静态属性举例
      4. 1.1.4. 静态变量内存解析:
      5. 1.1.5. static的注意点:
      6. 1.1.6. 如何判定属性和方法应该使用static关键字
        1. 1.1.6.1. 关于属性
        2. 1.1.6.2. 关于方法
      7. 1.1.7. 使用举例
      8. 1.1.8. 单例模式
        1. 1.1.8.1. 设计模式的说明
        2. 1.1.8.2. 单例模式
    2. 1.2. main()的使用说明
      1. 1.2.1. 如何将控制台获取的数据传给形参
      2. 1.2.2. 小结
    3. 1.3. 类的结构:代码块
      1. 1.3.1. 代码块的作用
      2. 1.3.2. 分类
      3. 1.3.3. 静态代码块
      4. 1.3.4. 非静态代码块
      5. 1.3.5. 实例化对象加载顺序
      6. 1.3.6. 属性的赋值顺序
    4. 1.4. 关键字:final
      1. 1.4.1. 可以用来修饰
      2. 1.4.2. 具体细节
    5. 1.5. 关键字:abstract
      1. 1.5.1. abstract
      2. 1.5.2. abstract修饰类
      3. 1.5.3. abstract修饰方法
      4. 1.5.4. 注意点
      5. 1.5.5. abstract的应用举例
      6. 1.5.6. 模板方法的设计模式
        1. 1.5.6.1. 解决的问题
        2. 1.5.6.2. 举例
      7. 1.5.7. 应用场景
    6. 1.6. 关键字:interface
      1. 1.6.1. 使用说明
        1. 1.6.1.1. 如何定义接口
      2. 1.6.2. 举例
      3. 1.6.3. 练习
      4. 1.6.4. 体会面向接口编程的思想
      5. 1.6.5. Java8中关于接口的新规范
      6. 1.6.6. 面试题
      7. 1.6.7. 面试题
      8. 1.6.8. 面试题
      9. 1.6.9. 代理模式
        1. 1.6.9.1. 解决的问题
        2. 1.6.9.2. 举例
        3. 1.6.9.3. 应用场景
      10. 1.6.10. 工厂的设计模式
        1. 1.6.10.1. 解决的问题
        2. 1.6.10.2. 具体模式
    7. 1.7. 类的结构:内部类
      1. 1.7.1. 定义
      2. 1.7.2. 内部类的分类
      3. 1.7.3. 成员内部类的理解
        1. 1.7.3.1. 注意
      4. 1.7.4. 如何创建成员内部类的对象
        1. 1.7.4.1. 如何在成员内部类中调用外部类的结构
      5. 1.7.5. 局部内部类的使用
      6. 1.7.6. 匿名内部类
|