1. 类和对象的内存分配机制
1.1 分配机制
Java 内存的结构分析
- 栈: 一般存放基本数据类型(局部变量)
- 堆: 存放对象(Cat cat , 数组等)
- 方法区:常量池(常量,比如字符串), 类加载信息
- 示意图 [Cat (name, age, price)]
Java 创建对象的流程简单分析
Person p = new Person();
p.name = “jack”;
p.age = 10
说明:
- 先加载 Person 类信息(属性和方法信息, 只会加载一次)
- 在堆中分配空间, 进行默认初始化(看规则)
- 把地址赋给 p , p 就指向对象
- 进行指定初始化, 比如 p.name =”jack” p.age = 10
2. 继承
2.1 super、this只能使用在构造方法中
super 在使用时,必须放在构造器第一行(super 只能在构造器中使用)
class Student{
public Student(){
}
public Student(int a){
this(); // 可以 调用无参构造
}
public void create(){
//super(); //错误,super只能在构造方法中调用
}
}
2.2 继承内存机制
package com.hspedu.extend_;
/**
* 讲解继承的本质
*/
public class ExtendsTheory {
public static void main(String[] args) {
Son son = new Son();//内存的布局
//?-> 这时请大家注意,要按照查找关系来返回信息
//(1) 首先看子类是否有该属性
//(2) 如果子类有这个属性,并且可以访问,则返回信息
//(3) 如果子类没有这个属性,就看父类有没有这个属性(如果父类有该属性,并且可以访问,就返回信息..)
//(4) 如果父类没有就按照(3)的规则,继续找上级父类,直到Object...
System.out.println(son.name);//返回就是大头儿子
//System.out.println(son.age);//返回的就是39
//System.out.println(son.getAge());//返回的就是39
System.out.println(son.hobby);//返回的就是旅游
}
}
class GrandPa { //爷类
String name = "大头爷爷";
String hobby = "旅游";
}
class Father extends GrandPa {//父类
String name = "大头爸爸";
private int age = 39;
public int getAge() {
return age;
}
}
class Son extends Father { //子类
String name = "大头儿子";
}
补充:
如果一个类的上一级父类有一个属性被private修饰,那么将无法访问该资源,即使这个类的爷爷类有也不能访问
2.3 重写和重载的区别
2.4 this 和 super的区别
2.5 关于私有化的资源是否能被继承
3. 多态
3.1 细节补充
属性没有重写之说!属性的值看编译类型
3.2 动态绑定机制
package com.hspedu.poly_.dynamic_;
public class DynamicBinding {
public static void main(String[] args) {
//a 的编译类型 A, 运行类型 B
A a = new B();//向上转型
System.out.println(a.sum());//?40 -> 30
System.out.println(a.sum1());//?30-> 20
}
}
class A {//父类
public int i = 10;
//动态绑定机制:
public int sum() {//父类sum()
return getI() + 10;//20 + 10
}
public int sum1() {//父类sum1()
return i + 10;//10 + 10
}
public int getI() {//父类getI
return i;
}
}
class B extends A {//子类
public int i = 20;
// public int sum() {
// return i + 20;
// }
public int getI() {//子类getI()
return i;
}
// public int sum1() {
// return i + 10;
// }
}
4. Object
4.1 equals方法
package com.hspedu.object_;
public class Equals01 {
public static void main(String[] args) {
A a = new A();
A b = a;
A c = b;
System.out.println(a == c);//true
System.out.println(b == c);//true
B bObj = a;
System.out.println(bObj == c);//true
int num1 = 10;
double num2 = 10.0;
System.out.println(num1 == num2);//基本数据类型,判断值是否相等
//equals 方法,源码怎么查看.
//把光标放在equals方法,直接输入ctrl+b
//如果你使用不了. 自己配置. 即可使用.
/*
//带大家看看Jdk的源码 String类的 equals方法
//把Object的equals方法重写了,变成了比较两个字符串值是否相同
public boolean equals(Object anObject) {
if (this == anObject) {//如果是同一个对象
return true;//返回true
}
if (anObject instanceof String) {//判断类型
String anotherString = (String)anObject;//向下转型
int n = value.length;
if (n == anotherString.value.length) {//如果长度相同
char v1[] = value;
char v2[] = anotherString.value;
int i = 0;
while (n-- != 0) {//然后一个一个的比较字符
if (v1[i] != v2[i])
return false;
i++;
}
return true;//如果两个字符串的所有字符都相等,则返回true
}
}
return false;//如果比较的不是字符串,则直接返回false
}
*/
"hello".equals("abc");
//看看Object类的 equals 是
/*
//即Object 的equals 方法默认就是比较对象地址是否相同
//也就是判断两个对象是不是同一个对象.
public boolean equals(Object obj) {
return (this == obj);
}
*/
/*
//从源码可以看到 Integer 也重写了Object的equals方法,
//变成了判断两个值是否相同
public boolean equals(Object obj) {
if (obj instanceof Integer) {
return value == ((Integer)obj).intValue();
}
return false;
}
*/
Integer integer1 = new Integer(1000);
Integer integer2 = new Integer(1000);
System.out.println(integer1 == integer2);//false
System.out.println(integer1.equals(integer2));//true
String str1 = new String("hspedu");
String str2 = new String("hspedu");
System.out.println(str1 == str2);//false
System.out.println(str1.equals(str2));//true
}
}
class B {}
class A extends B {}
4.2 finalize 方法
- 当对象被回收时,系统自动调用该对象的 finalize 方法。子类可以重写该方法,做一些释放资源的操作
- 什么时候被回收:当某个对象没有任何引用时,则 jvm 就认为这个对象是一个垃圾对象,就会使用垃圾回收机制来
销毁该对象,在销毁该对象前,会先调用 finalize 方法。 - 垃圾回收机制的调用,是由系统来决定(即有自己的 GC 算法), 也可以通过 System.gc() 主动触发垃圾回收机制
5. 静态方法(类方法)细节
package com.hspedu.static_;
public class StaticMethodDetail {
public static void main(String[] args) {
D.hi();//ok
//非静态方法,不能通过类名调用
//D.say();, 错误,需要先创建对象,再调用
new D().say();//可以
}
}
class D {
private int n1 = 100;
private static int n2 = 200;
public void say() {//非静态方法,普通方法
}
public static void hi() {//静态方法,类方法
//类方法中不允许使用和对象有关的关键字,
//比如this和super。普通方法(成员方法)可以。
//System.out.println(this.n1);
}
//类方法(静态方法)中 只能访问 静态变量 或静态方法
//口诀:静态方法只能访问静态成员.
public static void hello() {
System.out.println(n2);
System.out.println(D.n2);
//System.out.println(this.n2);不能使用
hi();//OK
//say();//错误
}
//普通成员方法,既可以访问 非静态成员,也可以访问静态成员
//小结: 非静态方法可以访问 静态成员和非静态成员
public void ok() {
//非静态成员
System.out.println(n1);
say();
//静态成员
System.out.println(n2);
hello();
}
}
5.1 构造方法可以调用静态和非静态资源
class Person {
private int id;
private static int total = 0;
public static int getTotalPerson() {
//id++;//错误, 注销
return total;
}
public Person() {//构造器
total++; //total = 1
id = total;//id = 1
}
}
class TestPerson {
public static void main(String[] args) {
System.out.println("Number of total is " +Person.getTotalPerson()); //0
Person p1 = new Person();
System.out.println( "Number of total is "+ Person.getTotalPerson()); //1
}
}
5.2 static修饰的方法不可以使用this/super等关键字
class Person {
private int id;
private static int total = 0;
public static void setTotalPerson(int total){
// this.total = total;//错误,因为在static方法中,不可以使用this 关键字
Person.total = total;
}
public Person() {//构造器
total++;
id = total;
}
//编写一个方法,输出total的值
public static void m() {
System.out.println("total的值=" + total);
}
}
class TestPerson {
public static void main(String[] args) {
Person.setTotalPerson(3);
new Person(); //最后 total的值就是4
Person.m();//看看输出的是不是4
}
}
小结:
记住两句话
(1) 静态方法,只能访问静态成员
(2) 非静态方法,可以访问所有的成员
(3) 在编写代码时,仍然要遵守访问权限规则
5.3 static修饰的方法不能被重写
5. 代码块
5.1 细节补充
package com.hspedu.codeblock_;
public class CodeBlockDetail01 {
public static void main(String[] args) {
//类被加载的情况举例
//1. 创建对象实例时(new)
// AA aa = new AA();
//2. 创建子类对象实例,父类也会被加载, 而且,父类先被加载,子类后被加载
// AA aa2 = new AA();
//3. 使用类的静态成员时(静态属性,静态方法)
// System.out.println(Cat.n1);
//static代码块,是在类加载时,执行的,而且只会执行一次.
// DD dd = new DD();
// DD dd1 = new DD();
//普通的代码块,在创建对象实例时,会被隐式的调用。
// 被创建一次,就会调用一次。
// 如果只是使用类的静态成员时,普通代码块并不会执行
System.out.println(DD.n1);//8888, 静态模块块一定会执行
}
}
class DD {
public static int n1 = 8888;//静态属性
//静态代码块
static {
System.out.println("DD 的静态代码1被执行...");//
}
//普通代码块, 在new 对象时,被调用,而且是每创建一个对象,就调用一次
//可以这样简单的,理解 普通代码块是构造器的补充
{
System.out.println("DD 的普通代码块...");
}
}
class Animal {
//静态代码块
static {
System.out.println("Animal 的静态代码1被执行...");//
}
}
class Cat extends Animal {
public static int n1 = 999;//静态属性
//静态代码块
static {
System.out.println("Cat 的静态代码1被执行...");//
}
}
class BB {
//静态代码块
static {
System.out.println("BB 的静态代码1被执行...");//1
}
}
class AA extends BB {
//静态代码块
static {
System.out.println("AA 的静态代码1被执行...");//2
}
}
5.2 静态代码块只能调用静态成员
6. 各方法代码块之间的执行顺序
package com.hspedu.codeblock_;
public class CodeBlockDetail04 {
public static void main(String[] args) {
//老师说明
//(1) 进行类的加载
//1.1 先加载 父类 A02 1.2 再加载 B02
//(2) 创建对象
//2.1 从子类的构造器开始
//new B02();//对象
new C02();
}
}
class A02 { //父类
private static int n1 = getVal01();
static {
System.out.println("A02的一个静态代码块..");//(2)
}
{
System.out.println("A02的第一个普通代码块..");//(5)
}
public int n3 = getVal02();//普通属性的初始化
public static int getVal01() {
System.out.println("getVal01");//(1)
return 10;
}
public int getVal02() {
System.out.println("getVal02");//(6)
return 10;
}
public A02() {//构造器
//隐藏
//super()
//普通代码和普通属性的初始化......
System.out.println("A02的构造器");//(7)
}
}
class C02 {
private int n1 = 100;
private static int n2 = 200;
private void m1() {
}
private static void m2() {
}
static {
//静态代码块,只能调用静态成员
//System.out.println(n1);错误
System.out.println(n2);//ok
//m1();//错误
m2();
}
{
//普通代码块,可以使用任意成员
System.out.println(n1);
System.out.println(n2);//ok
m1();
m2();
}
}
class B02 extends A02 { //
private static int n3 = getVal03();
static {
System.out.println("B02的一个静态代码块..");//(4)
}
public int n5 = getVal04();
{
System.out.println("B02的第一个普通代码块..");//(9)
}
public static int getVal03() {
System.out.println("getVal03");//(3)
return 10;
}
public int getVal04() {
System.out.println("getVal04");//(8)
return 10;
}
//一定要慢慢的去品..
public B02() {//构造器
//隐藏了
//super()
//普通代码块和普通属性的初始化...
System.out.println("B02的构造器");//(10)
// TODO Auto-generated constructor stub
}
}
7. final细节补充
7.1 final的修饰属性的赋值
package com.hspedu.final_;
public class FinalDetail01 {
public static void main(String[] args) {
CC cc = new CC();
new EE().cal();
}
}
class AA {
/*
1. 定义时:如 public final double TAX_RATE=0.08;
2. 在构造器中
3. 在代码块中
*/
public final double TAX_RATE = 0.08;//1.定义时赋值
public final double TAX_RATE2 ;
public final double TAX_RATE3 ;
public AA() {//构造器中赋值
TAX_RATE2 = 1.1;
}
{//在代码块赋值
TAX_RATE3 = 8.8;
}
}
class BB {
/*
如果final修饰的属性是静态的,则初始化的位置只能是
1 定义时 2 在静态代码块 不能在构造器中赋值。
*/
public static final double TAX_RATE = 99.9;
public static final double TAX_RATE2 ;
static {
TAX_RATE2 = 3.3;
}
}
//final类不能继承,但是可以实例化对象
final class CC { }
//如果类不是final类,但是含有final方法,则该方法虽然不能重写,但是可以被继承
//即,仍然遵守继承的机制.
class DD {
public final void cal() {
System.out.println("cal()方法");
}
}
class EE extends DD { }
7.1 权限修饰符和static修饰的常量不会导致类加载
package com.hspedu.final_;
public class FinalDetail02 {
public static void main(String[] args) {
System.out.println(BBB.num); //10000
//不会执行静态代码块,因为final和static修饰的常量不会导致类加载
//包装类,String 是final类,不能被继承
}
}
//final 和 static 往往搭配使用,效率更高,不会导致类加载.底层编译器做了优化处理
class BBB {
public final static int num = 10000;
static {
System.out.println("BBB 静态代码块被执行");
}
}
final class AAA{
//一般来说,如果一个类已经是final类了,就没有必要再将方法修饰成final方法
//public final void cry() {}
}
8. 抽象类
8.1 abstract只能修饰类或方法
8.2 细节2:抽象类本质还是类
8.3 抽象方法不能使用private/final/static修饰
package com.hspedu.abstract_;
public class AbstractDetail02 {
public static void main(String[] args) {
System.out.println("hello");
}
}
//抽象方法不能使用private、final 和 static来修饰,因为这些关键字都是和重写相违背的
abstract class H {
public abstract void hi();//抽象方法
}
//如果一个类继承了抽象类,则它必须实现抽象类的所有抽象方法,除非它自己也声明为abstract类
abstract class E {
public abstract void hi();
}
abstract class F extends E {
}
class G extends E {
@Override
public void hi() { //这里相等于G子类实现了父类E的抽象方法,所谓实现方法,就是有方法体
}
}
//抽象类的本质还是类,所以可以有类的各种成员
abstract class D {
public int n1 = 10;
public static String name = "韩顺平教育";
public void hi() {
System.out.println("hi");
}
public abstract void hello();
public static void ok() {
System.out.println("ok");
}
}
8.4 抽象类里的普通方法可以直接调用抽象方法
8.5 抽象类最佳实践-模板设计模式
8.5.1 基本介绍
8.5.2 模板设计模式能解决的问题
8.5.3 最佳实践
Template.java
abstract public class Template { //抽象类-模板设计模式
public abstract void job();//抽象方法
public void calculateTime() {//实现方法,调用job方法
//得到开始的时间
long start = System.currentTimeMillis();
job(); //动态绑定机制
//得的结束的时间
long end = System.currentTimeMillis();
System.out.println("任务执行时间 " + (end - start));
}
}
AA.java
public class AA extends Template {
//计算任务
//1+....+ 800000
@Override
public void job() { //实现Template的抽象方法job
long num = 0;
for (long i = 1; i <= 800000; i++) {
num += i;
}
}
// public void job2() {
// //得到开始的时间
// long start = System.currentTimeMillis();
// long num = 0;
// for (long i = 1; i <= 200000; i++) {
// num += i;
// }
// //得的结束的时间
// long end = System.currentTimeMillis();
// System.out.println("AA 执行时间 " + (end - start));
// }
}
BB.java
public class BB extends Template{
public void job() {//这里也去,重写了Template的job方法
long num = 0;
for (long i = 1; i <= 80000; i++) {
num *= i;
}
}
}
测试类
public class TestTemplate {
public static void main(String[] args) {
AA aa = new AA();
aa.calculateTime(); //这里还是需要有良好的OOP基础,对多态
BB bb = new BB();
bb.calculateTime();
}
}
9. 接口
9.1 JDK8.0接口类可以用静态方法、默认方法
9.2 接口中所有方法是public,抽象方法可以不用写abstract
package com.hspedu.interface_;
public class InterfaceDetail01 {
public static void main(String[] args) {
//new IA();
}
}
//1.接口不能被实例化
//2.接口中所有的方法是 public方法, 接口中抽象方法,可以不用abstract 修饰
//3.一个普通类实现接口,就必须将该接口的所有方法都实现,可以使用alt+enter来解决
//4.抽象类去实现接口时,可以不实现接口的抽象方法
interface IA {
void say();//修饰符 public protected 默认 private
void hi();
}
class Cat implements IA{
@Override
public void say() {
}
@Override
public void hi() {
}
}
abstract class Tiger implements IA {
}
9.3 接口修饰符、接口属性
package com.hspedu.interface_;
public class InterfaceDetail02 {
public static void main(String[] args) {
//老韩证明 接口中的属性,是 public static final
System.out.println(IB.n1);//说明n1 就是static
//IB.n1 = 30; 说明n1 是 final
}
}
interface IB {
//接口中的属性,只能是final的,而且是 public static final 修饰符
int n1 = 10; //等价 public static final int n1 = 10;
void hi();
}
interface IC {
void say();
}
//接口不能继承其它的类,但是可以继承多个别的接口
interface ID extends IB,IC {
}
//接口的修饰符 只能是 public 和默认,这点和类的修饰符是一样的
interface IE{}
//一个类同时可以实现多个接口
class Pig implements IB,IC {
@Override
public void hi() {
}
@Override
public void say() {
}
}
9.4 接口和类的区别以及使用场景
9.5 接口和类的综合练习
package com.hspedu.interface_;
public class InterfaceExercise02 {
public static void main(String[] args) {
}
}
interface A { // 1min 看看
int x = 0;
} //想到 等价 public static final int x = 0;
class B {
int x = 1;
} //普通属性
class C extends B implements A {
public void pX() {
//System.out.println(x); //错误,原因不明确x
//可以明确的指定x
//访问接口的 x 就使用 A.x
//访问父类的 x 就使用 super.x
System.out.println(A.x + " " + super.x);
}
public static void main(String[] args) {
new C().pX();
}
}
10. 内部类
10.1 局部内部类
package com.hspedu.innerclass;
/**
* 演示局部内部类的使用
*/
public class LocalInnerClass {//
public static void main(String[] args) {
//演示一遍
Outer02 outer02 = new Outer02();
outer02.m1();
System.out.println("outer02的hashcode=" + outer02);
}
}
class Outer02 {//外部类
private int n1 = 100;
private void m2() {
System.out.println("Outer02 m2()");
}//私有方法
public void m1() {//方法
//1.局部内部类是定义在外部类的局部位置,通常在方法
//3.不能添加访问修饰符,但是可以使用final 修饰
//4.作用域 : 仅仅在定义它的方法或代码块中
final class Inner02 {//局部内部类(本质仍然是一个类)
//2.可以直接访问外部类的所有成员,包含私有的
private int n1 = 800;
public void f1() {
//5. 局部内部类可以直接访问外部类的成员,比如下面 外部类n1 和 m2()
//7. 如果外部类和局部内部类的成员重名时,默认遵循就近原则,如果想访问外部类的成员,
// 使用 外部类名.this.成员)去访问
// 解读 Outer02.this 本质就是外部类的对象, 即哪个对象调用了m1, Outer02.this就是哪个对象
System.out.println("n1=" + n1 + " 外部类的n1=" + Outer02.this.n1);
System.out.println("Outer02.this hashcode=" + Outer02.this);
m2();
}
}
//6. 外部类在方法中,可以创建Inner02对象,然后调用方法即可
Inner02 inner02 = new Inner02();
inner02.f1();
}
}
10.2 匿名内部类
package com.hspedu.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() {//方法
//基于接口的匿名内部类
//老韩解读
//1.需求: 想使用IA接口,并创建对象
//2.传统方式,是写一个类,实现该接口,并创建对象
//3.老韩需求是 Tiger/Dog 类只是使用一次,后面再不使用
//4. 可以使用匿名内部类来简化开发
//5. tiger的编译类型 ? IA
//6. tiger的运行类型 ? 就是匿名内部类 Outer04$1
/*
我们看底层 会分配 类名 Outer04$1
class Outer04$1 implements IA {
@Override
public void cry() {
System.out.println("老虎叫唤...");
}
}
*/
//7. jdk底层在创建匿名内部类 Outer04$1,立即马上就创建了 Outer04$1实例,并且把地址
// 返回给 tiger
//8. 匿名内部类使用一次,就不能再使用
IA tiger = new IA() {
@Override
public void cry() {
System.out.println("老虎叫唤...");
}
};
System.out.println("tiger的运行类型=" + tiger.getClass());
tiger.cry();
tiger.cry();
tiger.cry();
// IA tiger = new Tiger();
// tiger.cry();
//演示基于类的匿名内部类
//分析
//1. father编译类型 Father
//2. father运行类型 Outer04$2
//3. 底层会创建匿名内部类
/*
class Outer04$2 extends Father{
@Override
public void test() {
System.out.println("匿名内部类重写了test方法");
}
}
*/
//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
father.test();
//基于抽象类的匿名内部类
Animal animal = new Animal(){
@Override
void eat() {
System.out.println("小狗吃骨头...");
}
};
animal.eat();
}
}
interface IA {//接口
public void cry();
}
//class Tiger implements IA {
//
// @Override
// public void cry() {
// System.out.println("老虎叫唤...");
// }
//}
//class Dog implements IA{
// @Override
// public void cry() {
// System.out.println("小狗汪汪...");
// }
//}
class Father {//类
public Father(String name) {//构造器
System.out.println("接收到name=" + name);
}
public void test() {//方法
}
}
abstract class Animal { //抽象类
abstract void eat();
}
package com.hspedu.innerclass;
public class AnonymousInnerClassDetail {
public static void main(String[] args) {
Outer05 outer05 = new Outer05();
outer05.f1();
//外部其他类---不能访问----->匿名内部类
System.out.println("main outer05 hashcode=" + outer05);
}
}
class Outer05 {
private int n1 = 99;
public void f1() {
//创建一个基于类的匿名内部类
//不能添加访问修饰符,因为它的地位就是一个局部变量
//作用域 : 仅仅在定义它的方法或代码块中
Person p = new Person(){
private int n1 = 88;
@Override
public void hi() {
//可以直接访问外部类的所有成员,包含私有的
//如果外部类和匿名内部类的成员重名时,匿名内部类访问的话,
//默认遵循就近原则,如果想访问外部类的成员,则可以使用 (外部类名.this.成员)去访问
System.out.println("匿名内部类重写了 hi方法 n1=" + n1 +
" 外部内的n1=" + Outer05.this.n1 );
//Outer05.this 就是调用 f1的 对象
System.out.println("Outer05.this hashcode=" + Outer05.this);
}
};
p.hi();//动态绑定, 运行类型是 Outer05$1
//也可以直接调用, 匿名内部类本身也是返回对象
// class 匿名内部类 extends Person {}
// new Person(){
// @Override
// public void hi() {
// System.out.println("匿名内部类重写了 hi方法,哈哈...");
// }
// @Override
// public void ok(String str) {
// super.ok(str);
// }
// }.ok("jack");
}
}
class Person {//类
public void hi() {
System.out.println("Person hi()");
}
public void ok(String str) {
System.out.println("Person ok() " + str);
}
}
//抽象类/接口...
补充:
匿名内部类可以引用于参数中(最佳实践)!!
10.3 成员内部类
package com.hspedu.innerclass;
public class MemberInnerClass01 {
public static void main(String[] args) {
Outer08 outer08 = new Outer08();
outer08.t1();
//外部其他类,使用成员内部类的三种方式
// 第一种方式
// outer08.new Inner08(); 相当于把 new Inner08()当做是outer08成员
// 这就是一个语法,不要特别的纠结.
Outer08.Inner08 inner08 = outer08.new Inner08();
inner08.say();
// 第二方式 在外部类中,编写一个方法,可以返回 Inner08对象
Outer08.Inner08 inner08Instance = outer08.getInner08Instance();
inner08Instance.say();
}
}
class Outer08 { //外部类
private int n1 = 10;
public String name = "张三";
private void hi() {
System.out.println("hi()方法...");
}
//1.注意: 成员内部类,是定义在外部内的成员位置上
//2.可以添加任意访问修饰符(public、protected 、默认、private),因为它的地位就是一个成员
public class Inner08 {//成员内部类
private double sal = 99.8;
private int n1 = 66;
public void say() {
//可以直接访问外部类的所有成员,包含私有的
//如果成员内部类的成员和外部类的成员重名,会遵守就近原则.
//,可以通过 外部类名.this.属性 来访问外部类的成员
System.out.println("n1 = " + n1 + " name = " + name + " 外部类的n1=" + Outer08.this.n1);
hi();
}
}
//方法,返回一个Inner08实例
public Inner08 getInner08Instance(){
return new Inner08();
}
//写方法
public void t1() {
//使用成员内部类
//创建成员内部类的对象,然后使用相关的方法
Inner08 inner08 = new Inner08();
inner08.say();
System.out.println(inner08.sal);
}
}
10.4 静态内部类
package com.hspedu.innerclass;
public class StaticInnerClass01 {
public static void main(String[] args) {
Outer10 outer10 = new Outer10();
outer10.m1();
//外部其他类 使用静态内部类
//方式1
//因为静态内部类,是可以通过类名直接访问(前提是满足访问权限)
Outer10.Inner10 inner10 = new Outer10.Inner10();
inner10.say();
//方式2
//编写一个方法,可以返回静态内部类的对象实例.
Outer10.Inner10 inner101 = outer10.getInner10();
System.out.println("============");
inner101.say();
Outer10.Inner10 inner10_ = Outer10.getInner10_();
System.out.println("************");
inner10_.say();
}
}
class Outer10 { //外部类
private int n1 = 10;
private static String name = "张三";
private static void cry() {}
//Inner10就是静态内部类
//1. 放在外部类的成员位置
//2. 使用static 修饰
//3. 可以直接访问外部类的所有静态成员,包含私有的,但不能直接访问非静态成员
//4. 可以添加任意访问修饰符(public、protected 、默认、private),因为它的地位就是一个成员
//5. 作用域 :同其他的成员,为整个类体
static class Inner10 {
private static String name = "韩顺平教育";
public void say() {
//如果外部类和静态内部类的成员重名时,静态内部类访问的时,
//默认遵循就近原则,如果想访问外部类的成员,则可以使用 (外部类名.成员)
System.out.println(name + " 外部类name= " + Outer10.name);
cry();
}
}
public void m1() { //外部类---访问------>静态内部类 访问方式:创建对象,再访问
Inner10 inner10 = new Inner10();
inner10.say();
}
public Inner10 getInner10() {
return new Inner10();
}
public static Inner10 getInner10_() {
return new Inner10();
}
}