内部类【可以直接访问私有属性,并且可以体现类与类之间的包含关系】
内部 ---> 外部类 | 外部类 ---> 内部 | 外部其它类 ---> 内部 | |
---|---|---|---|
局部内部类 | 直接访问 如果名称相同,使用 外部类.this.成员 访问外部类的成员 |
创建对象,再访问(注意:必须在作用域内) | 不能访问 ---> 局部内部类(因为 局部内部类地位是一个局部变量) |
匿名内部类【没有名字】 | 创建对象,再访问(注意:必须在作用域内) | ||
成员内部类(没有 staic 修饰) | 第一种方式 // outer001.new Inner001();相当于把 new Inner001()当作是 Outer001 成员 // 第二种方式 在外部类中,编写一个方法,可以返回一个Inner001对象 Outer001.Inner001 inner001Instance = outer001.getInner001Instance(); |
||
静态内部类(使用 static 修饰) | 重名的话: 使用 外部类名.成员【本质;静态内部类只能访问外部类的静态成员】 |
// 方式1 // 因为静态内部类,是可以通过类名直接访问(前提是满足访问权限(非private)) Outer002.Inner002 inner002 = new Outer002.Inner002().; inner002.say(); // 方式2 // 编写一个方法,可以返回静态内部类的对象实例 new Outer002().getInner002Instance().say(); |
类的五大成员:
- 属性
- 方法
- 构造器
- 代码块
- 内部类
位置 | 作用范围 | 默认值 | 所在内存位置 | 生命周期不同 | |
---|---|---|---|---|---|
成员变量 | 属于每个类,描述类的属性 | 作用于每个类 | 系统会根据该变量的类型赋予其相应的 默认值,不手动赋值,也不会报错 | 在对象创建以后存在于堆中,对象回收时,成员变量消失 | 随对象的创建而创建,对象回收时,成员变量消失 |
局部变量 | 在每个方法内部使用,出了方法不可用 | 作用在方法内部 | 声明之后系统不会赋予默认值,必须自己手动赋值,否则会报错 | 在方法被调用时存在于栈中,方法调执行结束,从栈中清除 | 随着方法的调用被创建,方法执行结束后,从栈中清除 |
成员变量 VS 局部变量【局部变量在使用前必须被程序员主动初始化值】
-
成员变量是指在一个类中定义的变量,比如:
public class Dog { // 这些都是成员变量,用于描述类本身所具有的一些属性 String breed; String name; String color; int age; }
-
局部变量是指在方法或者方法代码块中定义的变量,其作用域是其所在的代码块。可分为以下三种:
-
方法参数变量(形参):在整个方法内有效。
// 定义了一个 testFun() 方法,该方法中包含一个 int 类型的参数变量 n,其作用域是 testFun() 方法体内。当调用方法时传递进了一个参数 3,因此其输出控制台的 n 值是 3。 public class Test3 { public static void testFun(int n) { System.out.println("n=" + n); } public static void main(String[] args) { testFun(3); } }
-
方法局部变量(方法内定义): 从定义这个变量开始到方法结束这一段时间内有效。
// 声明两个局部变量并输出其值,其实现代码如下: public class Test2 { public static void main(String[] args) { int a = 7; if (5 > 3) { int s = 3; // 声明一个 int 类型的局部变量 System.out.println("s=" + s); System.out.println("a=" + a); } System.out.println("a=" + a); } } // 上述实例中定义了 a 和 s 两个局部变星,其中 int 类型的 a 的作用域是整个 main() 方法,而 int 类型的变量 s 的作用域是 if 语句的代码块内
-
代码块局部变量(代码块内定义):从定义这个变量开始到代码块结束这一段时间内有效。
// 代码块局部变量常用于 try catch 代码块中,成为异常处理参数变量。 // 异常处理参数变量的作用域是在异常处理块中,该变量是将异常处理参数传递给异常处理块,与方法参数变量类似。 // 声明一个异常处理语句,实现代码如下: public class Test4 { public static void test() { try { System.out.println("Hello!Exception!"); } catch (Exception e) { // 异常处理块,参数为 Exception 类型 e.printStackTrace(); } } public static void main(String[] args) { test(); } } // 在上述实例中定义了异常处理语句,异常处理块 catch 的参数为 Exception 类型的变量 e,作用域是整个 catch 块。
-
成员变量
修饰 | 访问 | 声明周期 | |
---|---|---|---|
全局变量 | 无 static | 对象名.变量名 | 只要对象被当做引用,实例变量就存在 |
静态变量 | 有 static | 类名.变量名 对象名.变量名 |
其生命周期取决于类的生命周期 类被GC回收时才会被销毁 |
1. 基本介绍
一个类的内部又完整地嵌套了另一个类结构,被嵌套的类称为内部类(in ner class)
嵌套其它类的类称为外部类(outer class)
内部类最大特点:可以 直接访问私有属性,并且可以体现类与类之间的包含关系
2. 基本语法
class Outer{ // 外部类
class Inner{ // 内部类
}
}
class Other{ // 外部其它类
}
3. 快速入门
package com.innerclass;
public class InnerClass01 { // 外部其他类
public static void main(String[] args) {
}
}
class Outer{ // 外部类
private int n1 = 100; // 属性
public Outer(int n1) { // 构造器
this.n1 = n1;
}
public void m1(){ // 方法
System.out.println("m1()");
}
// 代码块
{
System.out.println("代码块....");
}
class Inner{ // 内部类,在Outer类的内部
}
}
4. 内部类的分类
1. 定义在外部类局部位置上(比如方法内)
局部内部类(有类名)---> 访问外部【外部类名.this.成员】
局部内部类的使用
-
可以直接访问外部类的所有成员,包含私有的
-
不能添加访问修饰符,因为它的地位就是一个局部变量。局部变量是不能使用修饰符的,但是可以使用 final 修饰,因为局部变量也可以使用 final
-
作用域:仅仅定义它的方法,或者代码块中
-
局部内部类 --- 访问 ---> 外部类的成员 【访问方式:直接访问】
-
外部类 --- 访问 ---> 局部内部类的成员
访问方式:创建对象,再访问(注意:必须在作用域内)
-
记住:
- 局部内部类定义在方法中 / 代码块
- 作用域在方法体或者代码块中
- 本质仍然是一个类
-
外部其它类 --- 不能访问 ---> 局部内部类(因为 局部内部类地位是一个局部变量)
-
如果外部类和局部内部类的成员重名时,默认遵循就近原则,如果想访问外部类的成员变量,则可以使用
# 解读 外部类名.this.成员
// 解读 Outer01.this // 本质就是外部类的对象 System.out.println("n1 = " + n1 + "外部类的n1=" + Outer01.this.n1);
package com.innerclass;
/**
* 演示局部内部类的使用
*/
public class LocalInnerClass { // 外部其它类
public static void main(String[] args) {
// 演示一遍
Outer01 outer01 = new Outer01();
outer01.m1();
System.out.println("outer01的hashcode=" + outer01);
}
}
class Outer01 { // 外部类
private int n1 = 100;
private void m2() { // 私有方法
System.out.println("Outer01 m2()");
}
public void m1() { // 方法
// 1)局部内部类是定义在外部类的局部位置,通常在方法
// 3)不能添加访问修饰符,但是可以使用final 修饰【被final修饰后,该内部类就不能被其它类继承了】
// 4)作用域:仅仅在定义它的方法(本例中即:在m1()方法内有效)或代码块中
class Inner01 { // 局部内部类(本质仍然是一个类)
// 2)可以直接访问外部类的所有成员(属性,方法),包含私有的
private int n1 = 800;
private void m2(){
System.out.println("Inner01 m2");
}
public void f1() {
// 5) 局部内部类可以直接访问外部类的成员,比如下面 外部类 n1 和 m2()
// 7) 如果外部类和局部内部类的成员重名时,默认遵循就近原则,如果想访问外部类的成员,则可以使用外部类名.this.成员
System.out.println("n1 = " + n1 + "外部类的n1=" + Outer01.this.n1);
System.out.println("Outer01.this hashcode=" + Outer01.this);
Outer01.this.m2();
}
}
// 6)外部类在方法m1()中,可以创建Inner01的对象,然后调用方法即可
Inner01 inner01 = new Inner01();
inner01.f1();
}
}
其实在 main方法中,还是要先创建外部类,然后才能访问到内部类的
经过 debug得出:Outer01.this 就是 外部类对象
匿名内部类(没有类名)⭐===> 是一个对象【简化开发】。用完一次就销毁,运行类型:Outer04$1 依次递增
- 本质是类
- 内部类
- 该类没有名字
- 同时还是一个对象
需求引入:想使用 IA 接口,并创建对象
interface IA{ // 接口
publc void cry();
}
// 传统方式实现
class Tiger implements IA{
@Override
public void cry(){
System.our.println("老虎叫唤!!!");
}
}
// 调用
IA tiger = new Tiger();
tiger.cry();
这个 tiger 对象,我可能只会用到一次,就不用了 ===> 这样我在专门去写一个 Tiger 类就很不划算!!!
基本语法:
new 类或接口(参数列表){
类体
}
// 使用匿名内部类来简化开发
// 1. tiger 的编译类型:IA
// 2. tiger 的运行类型:就是匿名内部类。class com.innerclass.Outer04$1
IA tiger = new IA() {
@Override
public void cry() {
System.out.println("老虎叫唤...");
}
};
System.out.println("tiger的运行类型=" + tiger.getClass());
tiger.cry();
// 在底层可以理解为这样:
/*
我们看底层 会分配 类名 Outer04$1
class ×××× implements IA{
@Override
public void cry() {
System.out.println("老虎叫唤...")
}
*/
// jdk底层在创建匿名内部类 Outer04$1,立即马上就创建了 Outer04$1实例,并且把地址返回给tiger
// 匿名内部类只能使用一次,就不能再使用了 【说的是匿名内部类只能使用一次,在底层生成完了给你返回一个实例,这个类就没有了】
// 但是这个对象(tiger)你是可以反复使用的
演示匿名内部类的使用
package com.innerclass;
public class AnonymousInnerClass {
public static void main(String[] args) {
Outer04 outer04 = new Outer04();
outer04.method();
}
}
class Outer04 { // 外部类
private int n1 = 10; // 属性
public void method(){ // 方法
// 基于接口的匿名内部类
// 需求:想使用接口IA,并创建对象
// 传统方式:写一个类,实现该接口,并创建对象
// 可以使用匿名内部类来简化开发
// tiger的编译类型是:IA,
// tiger的运行类型是:就是匿名内部类 ===> Outer04$1
/*
我们看底层 会分配 类名 Outer04$1
class ×××× implements IA{
@Override
public void cry() {
System.out.println("老虎叫唤...")
}
*/
// jdk底层在创建匿名内部类 Outer04$1,立即马上就创建了 Outer04$1实例,并且把地址返回给tiger
// 匿名内部类只能使用一次,就不能再使用了 【说的是匿名内部类只能使用一次,在底层生成完了给你返回一个实例,这个类就没有了】
// 但是这个对象(tiger)你是可以反复使用的
IA tiger = new IA() {
@Override
public void cry() {
System.out.println("老虎叫唤...");
}
};
System.out.println("tiger的运行类型=" + tiger.getClass());
tiger.cry();
// 演示基于类的匿名内部类
// 分析
// 1)father 编译类型 Father
// 2)father 运行类型 Outer04$2
// 3)底层会创建匿名内部类
/*
class Outer04$2 extends Father{
}
*/
// 4)同时也直接返回了 匿名内部类 Outer04$2的对象
// 5)注意("jack")参数列表会传递给构造器
Father father = new Father("jack"){
@Override
public void test() {
System.out.println("匿名内部类重写了test方法");
}
};
System.out.println("father对象的运行类型=" + father.getClass()); // Outer04$2【按照匿名内部类顺序编号从1开始】
father.test();
// 基于抽象类的匿名内部类 (由于是抽象类,那么这个方法 eat() 就要求你必须重写)
Animal animal = new Animal() {
@Override
void eat() {
System.out.println("小狗吃骨头...");
}
};
System.out.println("animal对象的运行类型" + animal.getClass()); // Outer04$3
animal.eat();
}
}
interface IA { // 接口
public void cry();
}
class Father {
public Father(String name) { // 构造器
System.out.println("接收到name = " + name);
}
public void test() { // 方法
}
}
abstract class Animal { // 抽象类
abstract void eat();
}
匿名内部类使用细节:
因为匿名内部类既是一个类的定义,同时本身也是一个对象,因此从语法上看:
- 它既有类的特征
- 也有创建对象的特征
因此,有如下2种方式 调用匿名内部类方法
package com.innerclass;
public class AnonymousInnerClassDetail {
public static void main(String[] args) {
Outer05 outer05 = new Outer05();
outer05.f1();
}
}
class Outer05 {
private int n1 = 99;
public void f1() {
// 创建一个基于类的匿名内部类
// 不能添加访问修饰符,因为它的地位就是一个局部变量
Person person = new Person() {
private int n1 = 1;
@Override
public void hi() {
System.out.println("匿名内部类重写了 hi()方法");
System.out.println("n1 = " + Outer05.this.n1);
}
};
person.hi(); // 动态绑定,运行类型是 Outer05$1
// 也可以直接调用,匿名内部类本身也是返回对象
// class 匿名内部类 extends Person {}
new Person(){
@Override
public void hi() {
System.out.println("匿名内部类重写了 hi()方法,哈哈...");
System.out.println("n1 = " + n1);
}
}.ok("白");
}
}
class Person { // 类
public void hi() {
System.out.println("Person hi()");
}
public void ok(String str) {
System.out.println("Person ok() " + str);
}
}
匿名内部类最佳实践
-
将匿名内部类 当做实参 传递
package com.innerclass; public class InnerClassExercise01 { public static void main(String[] args) { // 当做实参直接传递,简洁高效!!! f1(new IQ() { @Override public void show() { System.out.println("这是一副名画..."); } }); } // 静态方法,形参是接口类型 public static void f1(IQ iq){ iq.show(); } } // 接口 interface IQ{ void show(); }
-
习题
package com.jjq; public class homework { public static void main(String[] args) { // 传入匿名内部类,而这个匿名内部类可以根据实际情况,来打印不同的信息 CellPhone cellPhone = new CellPhone(); cellPhone.alarmClock(new Bell() { @Override public void ring() { System.out.println("懒猪起床了"); } }); cellPhone.alarmClock(new Bell() { @Override public void ring() { System.out.println("小伙伴上课了"); } }); } } class CellPhone{ public void alarmClock(Bell bell){ // 形参是接口类型 bell.ring(); } } interface Bell{ void ring(); }
2. 定义在外部类的成员位置上
成员内部类(没有 static 修饰)
可以添加访问修饰符
- public
- porotected
- 默认
- private
package com.innerclass;
public class MemberInnerClass01 {
public static void main(String[] args) {
Outer001 outer001 = new Outer001(); // 外部类使用成员内部类
outer001.t1();
// 外部其它类,使用成员内部类的三种方式
// 解读
// 第一种方式
// outer001.new Inner001();相当于把 new Inner001()当作是 Outer001 成员
// 这就是一个语法,不用特别纠结
Outer001.Inner001 inner001 = outer001.new Inner001();
inner001.say();
// 第二种方式 在外部类中,编写一个方法,可以返回一个Inner001对象
Outer001.Inner001 inner001Instance = outer001.getInner001Instance();
inner001Instance.say();
// 第三种【是对第二种的简写】
new Outer001().getInner001Instance().say();
}
}
class Outer001 { // 外部类
private int n1 = 10;
public String name = "张三";
// 注意:成员内部类,是定义在外部类的成员位置上
class Inner001 { // 成员内部类
public void say(){
// 可以直接访问外部类的所有成员,包含私有的
System.out.println("Outer001 的 n1 = " + n1 + " Outer001 的 name " + name);
}
}
// 方法,返回一个Inner001的实例
public Inner001 getInner001Instance() {
return new Inner001();
}
// 写方法
public void t1() {
// 使用成员内部类
Inner001 inner001 = new Inner001();
inner001.say();
}
}
静态内部类(使用 static 修饰)
- 可以访问外部类的所有静态成员,包含私有的
- 可以添加任意访问修饰符(public, protected, 默认, private),因为它的地位就是一个成员
- 作用域:同其它成员,为整个整体
package com.innerclass;
public class StaticInnerClass01 {
public static void main(String[] args) {
// 外部其它类 使用静态内部类
// 方式1
// 因为静态内部类,是可以通过类名直接访问(前提是满足访问权限(非private))
Outer002.Inner002 inner002 = new Outer002.Inner002();
inner002.say();
// 方式2
// 编写一个方法,可以返回静态内部类的对象实例
new Outer002().getInner002Instance().say();
Outer002.getInner002Instance2().say();
}
}
class Outer002{ // 外部类
private int n1 = 10;
private static String name = "白白";
// Inner002 就是静态内部类
// 1)放在外部类的成员位置
// 2)使用static 修饰
// 3)可以直接访问外部类的所有静态成员,包含私有的,但是不能访问非静态成员
// 4)可以添加任意访问修饰符(public,protected,默认,private),因为它的地位就是一个成员
// 5)作用域:同其它的成员,为整个整体
static class Inner002{ // 内部类
public void say(){
System.out.println(name);
// 不能直接访问外部类的非静态成员
// System.out.println(n1);
}
}
public void show() {
// 外部类使用内部类
new Inner002().say();
}
// 写一个方法返回静态内部类的实例
public Inner002 getInner002Instance() {
return new Inner002();
}
// 编写一个静态方法返回 (静态内部类的实例)
public static Inner002 getInner002Instance2(){
return new Inner002();
}
}
标签:内部,void,System,public,println,class
From: https://www.cnblogs.com/aclq/p/17268503.html