IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> Java知识库 -> Java基础知识-------面向对象(4) -> 正文阅读

[Java知识库]Java基础知识-------面向对象(4)

1.static关键字

1.1static静态关键字的引入

1)创建一个Calculator计算器类,并在Calculator类中定义两个属性 price和color。
2)在Calculator类中定义一个方法, getSum(); 求和功能.
注意 : 成员变量使用 private 修饰.需要对外提供 setter 和 getter 公共访问的接口方法.

1.class Demo02 
2.{
3.	public static void main(String[] args) 
4.	{
5.		// 1. 创建一个计算器对象
6.		Calculator calc = new Calculator();
7.
8.		// 2. 为属性进行赋值
9.		calc.setColor("pink");
10.		calc.setPrice(20);
11.
12.		// 3. 输出查看属性的值
13.		System.out.println("color:"+calc.getColor()+", price:"+calc.getPrice());
14.
15.		// 4. 调用计算器求和的功能
16.		int sum = calc.getSum(10, 20);
17.		System.out.println("sum="+sum); // result = 30
18.	}
19.}
20.
21.// 定义一个计算器类
22.class Calculator
23.{
24.	// 属性
25.	private String color;	// 价格
26.	private int price;		// 颜色
27.
28.	// set 设置颜色
29.	public void setColor(String color)
30.	{
31.		this.color = color;
32.	}
33.
34.	// get 获取颜色
35.	public String getColor()
36.	{
37.		return this.color;
38.	}
39.
40.	// set 设置价格
41.	public void setPrice(int price)
42.	{
43.		// 需要对 price 价格参数进行逻辑判断
44.		if (price >= 0)
45.		{
46.			this.price = price;
47.		}
48.		else
49.		{
50.			this.price = 0;
51.		}
52.	}
53.
54.	// get 获取价格
55.	public int getPrice()
56.	{
57.		return this.price;
58.	}
59.
60.	// 方法 求和
61.	int getSum(int num1, int num2)
62.	{
63.		return num1 + num2;
64.	}
65.}

对比计算器类的 getSum() 求和方法和之前学生类的 introduce() 方法内部有什么区别 ?
我们创建对象的时候,系统在堆区中为对象分配空间,并进行对象的属性初始化,最后返回该对象在堆区内存中的首地址,并且所有的成员变量都会随着这个对象的创建而在堆区中存在,所有这些成员变量都是用来描述当前对象的.
1.学生类中介绍自己的方法 :

1.	// 对象方法,介绍自己
2.	void introduce()
3.	{
4.		System.out.println("大家好,我叫"+name+", 我今年"+age+"岁, "+gender);
5.	}

2.计算器类中求和的方法 :

1.// 方法 求和
2.	int getSum(int num1, int num2)
3.	{
4.		return num1 + num2;
5.	}

1.2static修饰方法和内存图解

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
请问: 那在方法压栈之前, 类中的 “方法” 首先存储在内存中的什么位置呢 ?
在这里插入图片描述
在这里插入图片描述
请问 : 为什么非静态方法(对象方法) 需要与对象进行绑定?其绑定的目的是什么?

思考 : 我们能不能在不创建对象的前提下就可以调用 getSum() 方法呢?
有时候我们希望在不创建对象的情况下就可以调用某个方法,换句话说也就是使该 方法不必和对象绑定在一起.

要实现这样的效果,只需要在类中定义的方法前面加上 static 关键字即可.我们称这种方法为静态方法.
调用方式 : 类名.方法名();
在这里插入图片描述
(非静态) 对象方法 和 (静态) 类方法 调用的区别 :
在这里插入图片描述
总结 :
如果在设计方法中,该方法中无需访问成员属性(成员变量)时,那么此时该方法就可以设计为 静态方法.
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

1.3static修饰成员变量

说明 : 有时候,我们希望某些特定的数据在内存中只有一份,而且能够被一个类的所有实例对象所共享.在一个Java类中,可以使用 static 关键字来修饰成员变量,该变量被称作静态变量.
静态变量被所有实例共享,可以使用 “类名.变量名” 的形式来赋值.

需求 : 例如某个学校所有学生共享同一个学校名称.此时完全不必在每个学生对象所占用的内存空间中都定义一个变量来表示学校名称.而可以使用 static 关键字在静态区定义一个表示学校名称的变量让所有对象来共享.

1.class Demo03 
2.{
3.	public static void main(String[] args) 
4.	{
5.		// 1. 创建两个学生对象
6.		Student stu1 = new Student();
7.		Student stu2 = new Student();
8.
9.		// 2. 为静态成员变量 schoolName 赋值
10.		Student.schoolName = "传智播客";
11.
12.		// 3. 输出查看
13.		System.out.println("stu1.shcoolName="+stu1.schoolName);
14.		System.out.println("stu2.shcoolName="+stu2.schoolName);
15.	}
16.}
17.
18.class Student
19.{
20.	// 定义一个静态成员变量用于存储学校名称
21.	public static String schoolName;	
22.}

在这里插入图片描述
静态属性被实例对象共享

1.4静态方法与非静态方法的使用注意事项

静态方法与非静态方法的区别1 : 调用格式
静态方法,也可称为类方法. 因为需要使用类名调用。 格式: 类名.方法名();
非静态的方法,也可称为对象方法。需要使用对象调用. 格式: 对象名.方法名();

静态方法与非静态方法的区别2:时间(类先于对象被加载,先有类,后有对象)
静态方法,它是在类加载,已经在方法区的静态区中完成了加载,使用(类名·方法名)就可以直接调用运行的方法。
非静态方法,它的运行必须是在类加载完成之后,通过new关键字在堆区中创建出对象,通过(对象名·方法名)才能调用。

静态方法与非静态方法的区别3:静态方法拥有(static)静态关键字,它是一个修饰符。
可以修饰类中的对象方法和成员属性。不能修饰构造方法。因为构造方法就是用来初始化对象的.
错误代码演示
报错异常图示
静态方法与非静态方法的区别4:调用关系不同。
静态方法中不能调用非静态方法,因为静态方法中没有 this, 无法确定是具体哪一个对象调用.
我的理解是,因为非静态方法需要在类new的时候随着对象一起在堆内存中创建,没有对象就不能去调用非静态方法this也是一个特殊对象,可惜静态方法里没有。我也尝试强行用this.非静态方法名,但是还是提示编译异常java: 无法从静态上下文中引用非静态 变量 this
错误代码演示异常演示
非静态方法中是可以调用静态方法的。因为 静态方法 可以直接使用类名调用, 无需确定具体哪个对象, 能够被该类所共享.
在这里插入图片描述
在这里插入图片描述
静态方法与非静态方法的区别5:静态方法中不能使用this 和 super关键字。
this关键字它表示的是当前调用这个方法的那个对象。而在静态方法中是没有对象的。
说明 :
无论是 非静态区 中的方法还是 静态区 中的方法, 如果想要被执行,那么都是要经过入栈和出栈的过程.

1.5静态代码块

静态代码块是定义在成员位置,使用static修饰的代码块

特点:
1.它优先于主方法执行、优先于构造代码块执行,当以任意形式第一次使用到该类时执行。
2.该类不管创建多少对象,静态代码块只执行一次。
3.可用于给静态变量赋值,用来给类进行初始化。

public class Person {
	private String name;
	private int age;
     //静态代码块
	static{
		System.out.println("静态代码块执行了");
	}
}

2.final 关键字

2.1final关键字的引入 :

1.class Demo 
2.{
3.	public static void main(String[] args) 
4.	{
5.		Dog d = new Dog();
6.		d.decleration();
7.	}
8.}
9.
10.// 定义一个 `Animal` 类
11.class Animal
12.{
13.	// 属性
15.	// 行为 : 动物类的联合声明
16.	void decleration()
17.	{
18.		System.out.println("动物类的联合声明: 我们是人类的好朋友.");
19.	}
20.}
21.
22.// 定义一个 `Dog` 类
23.class Dog extends Animal
24.{
25.	// 属性
27.	// 行为
28.}

在这里插入图片描述

1.class Demo 
2.{
3.	public static void main(String[] args) 
4.	{
5.		Fox f = new Fox();
6.		f.decleration();
7.	}
8.}
9.
10.// 定义一个 `Animal` 类
11.class Animal
12.{
13.	// 属性
14.
15.	// 行为 : 动物类的联合声明
16.	void decleration()
17.	{
18.		System.out.println("动物类的联合声明: 我们是人类的好朋友.");
19.	}
20.}
21.
22.// 定义一个 `Fox` 类
23.class Fox extends Animal
24.{
25.	// 属性
26.
27.	// 行为
28.	void decleration()
29.	{
30.		System.out.println("动物类的联合声明: 我们是人类的天敌.");
31.	}
32.}

在这里插入图片描述
继承的弊端 : 打破了封装性.不让子类重写该类的此声明,怎么能实现呢? 通过 Java中的一个关键字来实现. final(最终化).

2.2final关键字的特点 :

final 关键字是用于修饰类,变量和方法,它有"这是无法改变的"或者"最终"的含义.
特点 :
1.final 修饰的类不能被继承.即不能有子类.
2.final 修饰的方法不能被子类重写.老老实实继承,不允许做任何篡改.
3.final 修饰的变量(成员变量和局部变量)是常量,由于常量在运行期间不允许再发生改变,所以常量在声明时没有默认值,这就要求程序在声明常量时必须指定该常量的值.
由于final修饰的变量是常量,我们开发中为了和变量名有区别,因此所有的被final修饰的变量名统一大写
总结:不可继承、不可重写、不可更改。

  1. final 关键字修饰方法 (该方法将不可以被子类重写)
    错误代码及异常演示
  2. final 关键字修饰类 (该类将不可以被继承,也就是不能够派生子类)
    错误代码及异常演示
  3. final 关键字修饰局部变量 (Java 中被 final 修饰的变量为常量,它只能被赋值一次,也就是说final修饰的变量一旦被赋值,其值不能再次更改, 先定义,后赋值也是可以的.) 因为局部变量是没有默认初始值的
    在这里插入图片描述
    此处可以给为
void test(){
//此处是局部变量
final int NUMBER;
NUMBER=10;
NUMBER=20;
System.out.println("NUMBER"+NUMBER);
}
//一样也是会报错的。

java: 可能已分配变量NUMBER

  1. final 关键字修饰成员变量. 虚拟机不会对它进行初始化,因此使用final修饰成员变量时,需要程序员手动进行初始化,哪怕不使用,也需要进行手动初始化. (此时成员变量不能先定义,再赋值. 因为成员变量有默认初始值,然而虚拟机却不会对final修饰的成员变量赋初始值) 。
    在这里插入图片描述
    在这里插入图片描述
    特别注意点:
1.// 定义一个 `Fox` 类
2.class Fox extends Animal
3.{
4.	// 属性
5.	private final int age = 18;
6.	public static final double PI = 3.14;
7.
8.	// 静态代码块
9.	static
10.	{
11.		// PI = 3.14;	正确!
12.	}
13.
14.	// 构造代码块(只有一对大括号的这个就是构造代码块)
15.	{
16.		// age = 18;	正确!
17.	}
18.	
19.	// 构造方法
20.	public Fox()
21.	{
22.		// age = 20;	正确!
23.	}
24.
25.	// 行为
26.	void decleration()
27.	{
28.		System.out.println("动物类的联合声明: 我们是人类的天敌.");
29.	}
30.
31.	// 测试方法:
32.	void test()
33.	{
34.		final int num;
35.		num = 20;
36.
37.		// age = 18;	错误!
38.		
39.		System.out.println("num="+num);
40.	}
41.
42.	static void test2()
43.	{
44.		// PI = 3.14;  错误!
45.	}
46.}

总结 :
可以记忆为: 静态属性 / 成员属性可以在 静态代码块 / 构造代码块 / 构造方法 中可以先定义,后初始化, 是因为这些方法都是系统自己调用的.
但是不能在我们自己编写的 静态方法 / 对象方法 中进行初始化.因为程序无法确定我们自己编写的方法何时会被调用。

说明1 : 被 final 修饰的成员属性和局部变量即成为了常量, 存储在了 常量区. 没有初始值.
说明2 : 对象方法 / 静态方法 都需要被调用,在栈区中入栈,才能执行编写的代码.

3.包的声明与访问

3.1 包的概念

Java中的包,其实就是我们电脑系统中的文件夹,包里存放的是程序生成的.class文件。当.class文件很多的时候,通常我们会采用多个包进行存放管理他们,这种方式称为分包管理。
在项目中,我们将相同功能的类放到一个包中,方便管理。并且日常项目的分工也是以包作为边界。
类中声明的包必须与实际class文件所在的文件夹情况相一致,即类声明在a包下,则生成的.class文件必须在a文件夹下,否则,程序运行时会找不到类。

3.2 包的声明格式

通常使用公司网址反写,可以有多层包,包名采用全部小写字母,多层包之间用”.”连接
类中包的声明格式: package 包名.包名.包名…;

如:黑马程序员网址itheima.com那么网址反写就为com.itheima
传智播客 itcast.cn 那么网址反写就为 cn.itcast
注意:声明包的语句,必须写在程序有效代码的第一行(注释不算)
代码演示:

package cn.itcast; //包的声明,必须在有效代码的第一行
import java.util.Scanner;
import java.util.Random;
public class Demo {}

3.3 包的访问

在访问类时,为了能够找到该类,必须使用含有包名的类全名(包名.类名)。

//包名.包名….类名
/*
如: java.util.Scanner
     java.util.Random
	cn.itcast.Demo
带有包的类,创建对象格式:包名.类名 变量名 = new包名.类名();
*/
     cn.itcast.Demo d = new cn.itcast.Demo();

前提:包的访问与访问权限密切相关,这里以一般情况来说,即类用public修饰的情况。

类的简化访问:
当我们要使用一个类时,这个类与当前程序在同一个包中(即同一个文件夹中),或者这个类是java.lang包中的,通常可以省略掉包名,直接使用该类。

如:cn.itcast包中有两个类,PersonTest类,与Person类。我们在PersonTest类中,访问Person类时,由于是同一个包下,访问时可以省略包名,即直接通过类名访问 Person。

类名 变量名 = new类名();
Person p = new Person();

当我们要使用的类,与当前程序不在同一个包中(即不同文件夹中),要访问的类必须用public修饰才可访问。

package cn.itcst02;
public class Person {}

3.4 import导包

我们每次使用类时,都需要写很长的包名。很麻烦,我们可以通过import导包的方式来简化。
可以通过导包的方式使用该类,可以避免使用全类名编写(即,包类.类名)。
导包的格式:import 包名.类名;
当程序导入指定的包后,使用类时,就可以简化了。
演示如下

//导入包前的方式
//创建对象
java.util.Random r1 = new java.util.Random();
java.util.Random r2 = new java.util.Random();
java.util.Scanner sc1 = new java.util.Scanner(System.in);
java.util.Scanner sc2 = new java.util.Scanner(System.in);

//导入包后的方式
import java.util.Random;
import java.util.Scanner;
//创建对象
Random r1 = new Random();
Random r2 = new Random();
Scanner sc1 = new Scanner(System.in);
Scanner sc2 = new Scanner(System.in);

import导包代码书写的位置:在声明包package后,定义所有类class前,使用导包import包名.包名.类名;

4.四种访问权限修饰符

    了解了包的概念,就可以系统地介绍Java中的访问控制级别.
    **在Java中,针对类,成员方法和属性提供了四种访问级别.**
     分别是 **private, default, protected 和 public.**

在这里插入图片描述
说明 : 父类可以给拥有继承关系的子类提供一个特殊的权限 protected, 只有继承为子类后,就可以实现访问的权限.该权限只能子类使用. (继承关系)

5.内部类 Inner Class

在Java中,允许一个类的内部定义类,这样的类称作内部类,这个内部类所在的类称作外部类.根据内部类的位置,修饰符和定义的方式可分为
成员内部类, 静态内部类, 方法内部类. 匿名内部类.
类的三要素 : 1. 类名 2. 属性 3. 行为

6.1 成员内部类 (了解)

在一个类中除了可以定义成员变量,成员方法,还可以定义类.这样的类被称作成员内部类.外部类的成员变量在内部类中有效,这样使得内部类和外部类的交互更加方便.

内部类特点如下 :
1.在成员内部类中可以访问外部类的所有成员.
2.成员内部类中的方法也可以调用外部类的方法.
3.成员内部类中不可以声明静态变量,静态方法.因为成员变量是属于对象的,静态与对象无关.
4.外部类中可以用内部类声明成员属性,作为外部类的成员.也可以在方法中创建内部类对象,调用内部类的方法.

1.package cn.itcast;
2.
3.public class Demo {
4.
5.	public static void main(String[] args) {
6.		// 1. 创建一个 `Outer` 类的对象
7.		Outer out = new Outer();
8.		out.introduce();
9.	}
10.}
11.
12./*
13. * 成员内部类 :
14. * 1. 成员内部类可以访问外部类的所有成员.
15. * 2. 成员内部类的方法也可以调用外部类的方法.
16. * 3. 成员内部类不可以声明静态变量,静态方法.因为成员变量是属于对象的,静态与对象无关.
17. * 4. 外部类可以用内部类声明成员属性,作为外部类的成员.也可以在方法中创建内部类对象,调用内部类的方法.
18. */
19.
20.class Outer {
21.	// 属性
22.	private String name = "外部类的name属性";
23.	// 声明一个内部类类型的成员属性
24.	private Inner inner;
25.	
26.	// 定义了一个内部类,在成员属性的位置上. (成员内部类)
27.	class Inner {
28.		// 属性
29.		// 在成员内部中不能定义静态属性,和方法
30.		// private static String schoolName;
31.		// public static void show2() { }
32.		// 行为
33.		public void show() {
34.			System.out.println("我是成员内部类的show方法.");
35.			System.out.println("在内部类中访问外部类的成员属性:"+name);
36.			// 在成员内部类中调用外部类的introduce()方法
37.			// introduce();
38.		}
39.	}
40.	
41.	// 行为
42.	public void introduce()	{
43.		System.out.println("大家好,我是外部类的introduce()方法.");
44.		// 创建内部类对象,并调用内部类的方法
45.		Inner in = new Inner();
46.		in.show();
47.	}
48.}

在这里插入图片描述
内部类在生成class字节码文件时,这个类就变得有所属了.
在这里插入图片描述
如果想通过外部类去访问内部类,则需要通过外部类对象去创建内部类对象,创建内部类对象的具体语法格式如下 :

1.外部类名.内部类名 变量名 = new 外部类名().new 内部类名();
1.package cn.itcast;
2.
3.public class Demo {
4.
5.	public static void main(String[] args) {
6.		// 1. 创建一个 `Outer` 类的对象
7.		Outer out = new Outer();
8.		out.introduce();
9.		
10.		// 2. 创建一个 `Inner` 类的对象
11.		// 格式: 外部类名.内部类名 对象名 = new 外部类名().new 内部类名();
12.		Outer.Inner in = new Outer().new Inner();
13.		in.show();
14.	}
15.}
16.
17./*
18. * 成员内部类 :
19. * 1. 成员内部类可以访问外部类的所有成员.
20. * 2. 成员内部类的方法也可以调用外部类的方法.
21. * 3. 成员内部类不可以声明静态变量,静态方法.因为成员变量是属于对象的,静态与对象无关.
22. * 4. 外部类可以用内部类声明对象,作为外部类的成员.也可以在方法中创建内部类对象,调用内部类的方法.
23. */
24.
25.class Outer {
26.	// 属性
27.	private String name = "外部类的name属性";
28.	// 声明一个内部类类型的成员属性
29.	private Inner inner;
30.	
31.	// 定义了一个内部类,在成员属性的位置上. (成员内部类)
32.	class Inner {
33.		// 属性
34.		// 在成员内部中不能定义静态属性,和方法
35.		// private static String schoolName;
36.		// public static void show2() { }
37.		// 行为
38.		public void show() {
39.			System.out.println("我是成员内部类的show方法.");
40.			System.out.println("在内部类中访问外部类的成员属性:"+name);
41.			// 在成员内部类中调用外部类的introduce()方法
42.			introduce();
43.		}
44.	}
45.	
46.	// 行为
47.	public void introduce()	{
48.		System.out.println("大家好,我是外部类的introduce()方法.");
49.		// 创建内部类对象,并调用内部类的方法
50.		Inner in = new Inner();
51.		// in.show();
52.	}
}

在这里插入图片描述
注意 : 如果内部类被声明为私有,外界将无权访问.
在这里插入图片描述
在这里插入图片描述
说明 : 使用public不多见, 因为更多时候,内部类已经被封装到外部类中,不直接对外提供.

6.2 静态内部类 (了解)

可以使用static关键字来修饰一个成员内部类,该内部类被称作静态内部类,它可以在不创建外部类对象的情况下被实例化.静态内部类类似于静态变量,在类被加载时而完成加载,不与外部类的对象进行绑定.因此,无需先创建外部类对象,再由外部类对象来创建内部类对象.
创建静态内部类对象语法格式如下:
外部类名.内部类名 变量名 = new 外部类名.内部类名();

静态内部类具备如下特点 :
1.在静态内部类中只能访问外部类的静态成员.
2.在静态内部类中可以定义静态成员和非静态成员,而在成员内部类中不允许定义静态的成员.
3.在静态内部类的方法中不可以调用外部类的对象方法,但是可以调用外部类的静态方法.

1.class Demo 
2.{
3.	public static void main(String[] args) 
4.	{
5.		Outer.Inner in = new Outer.Inner();
6.		in.show();
7.	}
8.}
9.
10.class Outer				// 外部类
11.{
12.	// 属性
13.	private String name = "外部类的 name 属性";
14.	public static String schoolName = "传智播客";
15.
16.	/*
17.	静态内部类具备如下特点 :
18.	1.	在静态内部类中只能访问外部类的静态成员.
19.	2.	在静态内部类中可以定义静态成员和非静态成员,而在成员内部类中不允许定义静态的成员.
20.	3.	在静态内部类的方法中不可以调用外部类的对象方法,但是可以调用外部类的静态方法.
21.
22.	*/
23.	static class Inner
24.	{
25.		// 属性
26.		int age;
27.		public static double PI = 3.14;
28.
29.		// 行为
30.		void show()
31.		{
32.			System.out.println("在静态内部类中访问外部类的静态成员: schoolName="+schoolName);
33.			// 在静态内部类中调用外部类的对象方法
34.			// introduce();  错误!
35.			int sum = getSum(10, 20);
36.			System.out.println("sum="+sum);
37.		}
38.	}
39.
40.	
41.	// 行为
42.	void introduce()
43.	{
44.		System.out.println("大家好,我是外部类的方法introduce().");
45.	}
46.
47.	// 静态方法
48.	static int getSum(int num1, int num2)
49.	{
50.		return num1 + num2;
51.	}
52.}

6.3 方法内部类 (了解)

方法内部类是指在成员方法中定义的类,它只能在当前方法中被使用.

注意 : 下面代码在Outer类的introduce()方法中定义了一个方法内部类Inner3,由于Inner3是方法内部类,因此程序只能在方法中创建该类的实例对象并调用introduce() 方法.

1.class Demo 
2.{
3.	public static void main(String[] args) 
4.	{
5.		Outer out = new Outer();
6.		out.introduce();
7.	}
8.}
9.
10.class Outer				// 外部类
11.{
12.	// 属性
13.	private String name = "外部类的 name 属性";
14.	public static String schoolName = "传智播客";
15.
16.	// 行为
17.	void introduce()
18.	{
19.		System.out.println("大家好,我是外部类的方法introduce().");
20.
21.		// 定义一个 `方法内部类`
22.		class Inner
23.		{
24.			// 属性
25.			private String name;
26.			private int age;
27.
28.			// 行为
29.			void introduce()
30.			{
31.				System.out.println("大家好, 我叫"+name+", 我今年"+age+"岁了.");
32.			}
33.		}
34.
35.		// 在该方法结束之后,创建该类的对象,并实现调用
36.		Inner in = new Inner();
37.		in.name = "Jack";
38.		in.age = 30;
39.		in.introduce();
40.	}
41.}

6.4 匿名内部类 (重点)

在前面多态和接口的作用描述案例中,关于电脑和USB的故事.我们讲解了如果方法的参数被定义为接口类型.那么就需要定义一个类来实现接口,并根据该类进行对象实例化.除此之外,还可以使用匿名内部类来实现接口.

第一步 : 使用面向接口编程的思想实现笔记本电脑运行,外围设备工作的功能.

package cn.itcast;
public interface USB {
	// 定义接口方法
public void open();
	public void close();
}
1.package cn.itcast;
2.
3.public class Computer {
4.	// 属性
5.	private String CPU;
6.	private int memory;
7.	private int harddisk;
8.	
9.	// 行为
10.	public void run() {
11.		System.out.println("Computer run...");
12.	}
13.	
14.	// 需要给外围设备预留接口 (扩展功能)
15.	public void useUSB(USB usb) {
16.		if (usb != null) {
17.			usb.open();
18.			usb.close();
19.		}
20.	}
21.}
1.package cn.itcast;
2.
3.public class Mouse implements USB {
4.	// 属性
5.	
6.	// 行为
7.	public void open() {
8.		System.out.println("Mouse open...");
9.	}
10.	public void close() {
11.		System.out.println("Mouse close...");
12.	}
13.
14.}
1.package cn.itcast;
2.
3./*
4. * 案例1 : 使用面向接口编程的思想实现笔记本电脑运行,外围设备工作的功能.
5. */
6.
7.public class Demo4 {
8.
9.	public static void main(String[] args) {
10.		// 1. 创建一个 `Computer` 类的对象
11.		Computer com = new Computer();
12.		com.run();
13.		com.useUSB(new Mouse());
14.	}
15.
16.}

第二步 : 使用方法内部类,同样可以完成该功能.

只不过就是将普通类定义在了主方法的内部而已.

1.package cn.itcast;
2.
3./*
4. * 案例1 : 使用面向接口编程的思想实现笔记本电脑运行,外围设备工作的功能.
5. * 案例2 : 使用方法内部类,同样可以完成该功能. (main方法)
6. * 案例3 : 使用匿名内部类的书写格式.
7. */
8.
9.public class Demo4 {
10.
11.	public static void main(String[] args) {
12.		// 1. 创建一个 `Computer` 类的对象
13.		Computer com = new Computer();
14.		com.run();
15.		com.useUSB(new Mouse());
16.		
17.		// 在main方法中创建一个类  (方法内部类)
18.		class Keyboard implements USB {
19.			// 属性
20.			
21.			// 行为
22.			public void open() {
23.				System.out.println("Keyboard open...");
24.			}
25.			public void close() {
26.				System.out.println("Keyboard close...");
27.			}
28.		}
29.		
30.		com.useUSB(new Keyboard());
31.	}
32.
33.}

第三步 : 使用匿名内部类完成该功能.

匿名内部类的格式 :
new 父类() 或 接口() {
// 匿名内部类的实现代码
}

1.package cn.itcast;
2.
3./*
4. * 案例1 : 使用面向接口编程的思想实现笔记本电脑运行,外围设备工作的功能.
5. * 案例2 : 使用方法内部类,同样可以完成该功能. (main方法)
6. * 案例3 : 使用匿名内部类的书写格式.
7. */
8.
9.public class Demo4 {
10.
11.	public static void main(String[] args) {
12.		// 1. 创建一个 `Computer` 类的对象
13.		Computer com = new Computer();
14.		com.run();
15.		com.useUSB(new Mouse());
16.		
17.		// 在main方法中创建一个类  (方法内部类)
18.		class Keyboard implements USB {
19.			// 属性
20.			
21.			// 行为
22.			public void open() {
23.				System.out.println("Keyboard open...");
24.			}
25.			public void close() {
26.				System.out.println("Keyboard close...");
27.			}
28.		}
29.		
30.		com.useUSB(new Keyboard());
31.		
32.		// 说明1: new USB() 表示创建一个`接口`类型的对象. 接口不能创建对象,接口只能够被实现.
33.		// 说明2: new USB() {...} 表示实现接口.它就是一个接口的实现类. ... 表示实现接口中的代码
34.		// 请问: 这个接口的实现类有名称吗? 没有名称. 因此这种方式就称为 `匿名内部类`
35.		/*
36.		 	匿名内部类的格式 :
37.		 	new 父类() 或 接口() {
38.		 		// 匿名内部类的实现代码 
39.		 	} 
40.		 */
41.		com.useUSB(new USB() {
42.			// 需要实现接口中的方法
43.			public void open() {
44.				System.out.println("CameraVideo open...");
45.			}
46.			public void close() {
47.				System.out.println("CameraVideo close...");
48.			}
49.		});
50.	}
51.
52.}

我们发现只要是USB的实现类对象,无论叫什么名字都可以传入给USB接口类型作为参数.Mouse, Keyboard, CameraVideo …如果这个实现类对象没有名字呢? 是否只要实现了USB中接口的方法就可以传入了呢?

说明 :
1.调用c.useUSB() 方法时,在方法的参数位置上写 new USB() { } 这就相当于创建了一个USB接口的一个实现类对象. new USB() 后面有一对大括号,表示创建的对象为USB的实现类对象.该实现类是匿名的.
2.在大括号中编写匿名实现类的实现代码就可以了.
在这里插入图片描述
案例二 : 有一个Animal的抽象父类,其中有shout()抽象方法.在主类中有一个动物之声的函数,其参数为Animal类型的对象.需要使用匿名内部实现动物之声方法的调用.

实现方式一 : 子类实现.

1.package cn.itcast;
2.
3.public abstract class Animal {
4.	// 属性
5.	
6.	// 行为
7.	public abstract void shout();
8.
9.}
1.package cn.itcast;
2.
3.public class Dog extends Animal {
4.	// 属性
5.	
6.	// 行为
7.	public void shout()	 {
8.		System.out.println("汪汪汪...");
9.	}
10.
11.}
1.package cn.itcast;
2.
3.public class Demo5 {
4.
5.	public static void main(String[] args) {
6.		// 1. 创建一个 `Dog` 类的对象
7.		Dog d = new Dog();
8.		
9.		// 2. 调用函数,实现动物之声.
10.		soundOfAnimal(d);
11.
12.	}
13.	
14.	// 函数 : 动物之声
15.	public static void soundOfAnimal(Animal a) {
16.		a.shout();
17.	}
18.
19.}

实现方式二 : 方法内部类实现.

1.package cn.itcast;
2.
3.public class Demo05 {
4.
5.	public static void main(String[] args) {
6.		
7.		// 1. 创建一个 `Dog` 类的对象
8.		Dog d = new Dog();
9.		
10.		// 调用函数
11.		soundOfAnimals(d);
12.		
13.		// 方法内部类
14.		class Cat extends Animal {
15.			// 属性
16.			
17.			// 行为
18.			void shout() {
19.				System.out.println("喵喵喵...");
20.			}
21.		}
22.		
23.		
24.		// 2. 创建一个 `Cat` 类的对象
25.		Cat c = new Cat();
26.		soundOfAnimals(c);
20.	}
21.	
22.	// 函数 : 动物之声
23.	public static void soundOfAnimal(Animal a) {
24.		a.shout();
25.	}
27.
28.}

实现方式三 : 匿名内部类实现.

说明 :
1.调用soundOfAnimal () 方法时,在方法的参数位置上写 new Animal() { }这就相当于创建了一个实例对象 . 并将对象作为参数传给soundOfAnimal()方法.在new Animal() { } 后面有一对大括号,表示创建的对象为Animal的子类实例. 该子类是匿名的.

2.在大括号中编写匿名子类的实现代码就可以了.

package cn.itcast;
public class Demo05 {
	public static void main(String[] args) {
		// 匿名内部类实现
		// new Animal(); 抽象类不能实例化.
	// new Animal() { code ... }
		soundOfAnimals(new Animal() { 
		// code ... 
		void shout() {
			System.out.println("嗷嗷嗷...");
		}
		});
	}
// 功能: 动物之声
public static void soundOfAnimals(Animal a) {
	a.shout();
   }
}

当然,如果抽象类的父类仅仅只有一两个抽象方法的话,我们也同样可以使用匿名内部类来实现.但是注意,无论内部类还是匿名内部类,方法不要过多,否则阅读性会很差.

匿名内部类 :
1.接口的实现类.
2.抽象类的子类对象.
匿名内部类的格式 :
new 父类() 或 接口() {
// 内部类实现部分
}

6.5 关于匿名内部类的面试题

1.package cn.itcast;
2.
3.public class InterviewDemo {
4.
5.	public static void main(String[] args) {
6.		
7.		new Object() {
8.			public void show() {
9.				System.out.println("show ...");
10.			}
11.		}.show();
12.		
13.
14.		Object obj = new Object() {
15.			public void show() {
16.				System.out.println("show ...");
17.			}
18.		};
19.		obj.show();
20.
21.	}
22.}

详细说明与注释 :

1.package cn.itcast;
2.
3.public class InterviewDemo {
4.
5.	public static void main(String[] args) {
6.		
7.		// new Object() { } 表示此类是一个Object类的子类,匿名而已.
8.		new Object() {
9.			public void show() {
10.				System.out.println("show ...");
11.			}
12.		}.show();
13.		// 以上采用的是匿名内部类来完成调用.show方法是该子类特有的方法,直接使用子类自己的对象调用.
14.		
15.		// 这里发生了多态
16.		Object obj = new Object() {
17.			public void show() {
18.				System.out.println("show ...");
19.			}
20.		};
21.		// 多态调用方法,编译要看父类中是否有show方法,而Object类中没有show方法,因此编译失败!
22.		obj.show();
23.
24.	}
25.}

在这里插入图片描述

匿名内部类的局限性 : 由于匿名内部类没有类名称.因此在多态语句中无法向下转型.

  Java知识库 最新文章
计算距离春节还有多长时间
系统开发系列 之WebService(spring框架+ma
springBoot+Cache(自定义有效时间配置)
SpringBoot整合mybatis实现增删改查、分页查
spring教程
SpringBoot+Vue实现美食交流网站的设计与实
虚拟机内存结构以及虚拟机中销毁和新建对象
SpringMVC---原理
小李同学: Java如何按多个字段分组
打印票据--java
上一篇文章      下一篇文章      查看所有文章
加:2022-01-24 10:41:48  更:2022-01-24 10:44:10 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/24 9:42:05-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码