文章目录
类变量和类方法
类变量
不管static变量在哪里,共识(1)static变量是同一个类所有对象共享(2)static类变量,在类加载的时候就生成了。
类变量也叫静态变量/静态属性,是该类的所有对象共享的变量,任何一个该类的对象去访问它时,取到的都是相同的值,同样任何一个该类的对象去修改它时,修改的也是同一个变量。
类变量是随着类的加载而创建,所以即使没有创建对象实例也可访问。
语法
访问修饰符 static 数据类型 变量名;【推荐】
static 访问修饰符 数据类型 变量名;
访问
类名.类变量名 或者 对象名.类变量名
细节
- 当我们需要让某个类的所有对象都共享一个变量时,就可以考虑使用类变量(静态变量),比如学生类,统计所有学生的学费。
- 类变量是该类的所有对象共享的,而实例变量是每个对象独享的。
- 加上static称为类变量或静态变量,否则称为实例变量/普通变量/非静态变量。
- 类变量可以通过类名.类变量名【推荐】 或者 对象名.类变量名来访问。
- 实例变量不能通过 类名.类变量名 方式来访问。
- 类变量是随着类的加载而创建,所以即使没有创建对象实例也可访问。
- 类变量的声明周期是随类的加载而开始,随类的消亡而消失。
类方法
类方法也叫静态方法。
语法
访问修饰符 static 数据返回类型 方法名(){};【推荐】
static 访问修饰符 数据返回类型 方法名(){};
调用
类名.类方法名 或者 对象名.类方法名
细节
- 静态方法可以访问静态属性
- 类方法和普通方法都是随着类的加载而加载,将结构信息存储在方法区,类方法中无this的参数,普通方法中隐含着this的参数
- 类方法可以通过类名调用,也可以通过对象名调用
- 普通方法和对象有关,需要通过对象名调用,比如对象名.方法名(参数),不能通过类名调用
- 类方法中不允许使用和对象有关的关键字,比如this和super,普通方法(成员方法)可以
- 类方法(静态方法)中只能访问【静态变量】或【静态方法】
- 普通成员方法,既可以访问普通变量(方法),也可以访问静态变量(方法)
- 静态方法只能访问静态的变量,非静态的方法,可以访问静态成员和非静态成员
class Person {
private int id;
private static int total = 0;
public static int getTotalPerson(){
//id++;//非静态变量,静态方法不能访问非静态变量
return total;
}
public static void setTotalPerson(int total){
//this.total = total;//静态方法中不能使用this super
Person.total = total;
}
public Person(){
total++;
id = total;
}
}
public class TestPerson {
public static void main(String[] args) {
System.out.println(Person.getTotalPerson());//0
Person p1 = new Person();
System.out.println(Person.getTotalPerson());//1
Person.setTotalPerson(3);
new Person();
System.out.println(Person.getTotalPerson());//4
}
}
理解main方法语法
public static void main(String[] args) {}
- Java虚拟机需要调用类的main()方法,所以该方法的访问权限必须是publiic
- java虚拟机在执行main()方法时不必创建对象,所以该方法必须是static
- 该方法接收String类型的数组参数,该数组中保存执行java命令时传递给所运行的类的参数
- java执行的程序 参数1 参数2 参数3
- 在main()方法中,可以直接调用main方法所在类的静态方法或静态属性
- 但是,不能直接访问该类中的非静态成员,必须创建该类的一个实例对象后,才能通过这个对象去访问类的非静态成员
public class Test {
public int a;
public static int b;
public void f(){
System.out.println("f() a = " + a);
System.out.println("f() b = " + b);
}
public static void g(){
//System.out.println("f() a = " + a);//静态方法访问非静态属性
System.out.println("f() b = " + b);
}
public static void main(String[] args) {
System.out.println(b);
g();
Test test = new Test();//main()方法访问本类中的非静态变量或属性利用实例对象
System.out.println(test.a);
test.f();
for (int i = 0; i < args.length; i++) {
System.out.println(args[i] + "\t");
}
}
}
在右上角选择类那里选择Edit Configurations-------Program arguments传值
代码块
代码块又叫初始化块,属于类中的成员【即 是类的一部分】,类似于方法,将逻辑语句封装在方法体中,通过{}包围起来。
但和方法不同,没有方法名,没有返回,没有参数,只有方法体,而且不用通过对象或类显式调用,而是加载类时,或者创建对象时隐式调用。
语法
[修饰符]{
代码
};
- 修饰符可选,要写的话,也只能写static
- 代码块分为两类,使用static修饰的叫静态代码块,没有static修饰的叫普通代码块/非静态代码块
- 逻辑语句可以为任何逻辑语句(输入、输出、方法调用、循环、判断等)
- ;号可以写上,也可以省略
好处
- 相当于另外一种形式的构造器(对构造器的补充机制),可以做初始化的操作
- 场景:如果多个构造器中都有重复的语句,可以抽取到初始化块中,提高代码的重用性
public class CodeBlock {
public static void main(String[] args) {
Movie abc = new Movie("ABC");
}
}
class Movie{
private String name;
private double price;
private String director;
//将构造器中重复的语句放入代码块中
//不管调用哪个构造器,都会先调用代码块中的内容
//代码块中的顺序优先于构造器
{
System.out.println("电影屏幕打开");
System.out.println("广告开始");
System.out.println("电影正式开始");
}
public Movie(String name) {
// System.out.println("电影屏幕打开");
// System.out.println("广告开始");
// System.out.println("电影正式开始");
System.out.println("Movie(String name)");
this.name = name;
}
public Movie(String name, double price) {
// System.out.println("电影屏幕打开");
// System.out.println("广告开始");
// System.out.println("电影正式开始");
System.out.println("Movie(String name, double price)");
this.name = name;
this.price = price;
}
public Movie(String name, double price, String director) {
// System.out.println("电影屏幕打开");
// System.out.println("广告开始");
// System.out.println("电影正式开始");
System.out.println("Movie(String name, double price, String director)");
this.name = name;
this.price = price;
this.director = director;
}
}
细节
- static代码块也叫静态代码块,作用就是对类进行初始化,而且随类的加载而执行,并且只会执行一次,如果是普通代码块,每创建一个对象,就执行
- 类什么时候被加载
-
- 创建对象实例时
- 创建子类对象实例,父类也会被加载
- 使用类的静态成员时(静态属性、静态方法)
- 普通的代码块,在创建对象实例时,会被隐式的调用,被创建一次,就会调用一次;如果只是使用类的静态成员时,普通代码块并不会执行
- 创建一个对象时,在一个类调用顺序是:
-
- 调用静态代码块和静态属性初始化(静态代码块和静态属性初始化调用的优先级一样,如果有多个静态代码块和多个静态变量初始化,则按他们定义的顺序调用)
- 调用普通代码块和普通属性的初始化(普通代码块和普通属性初始化调用的优先级一样,如果有多个普通代码块和多个普通属性初始化,则按他们定义的顺序调用)
- 调用构造方法
- 构造方法(构造器)的最前面其实隐含了super()和调用普通代码块,静态相关的代码块,属性初始化,在类加载时,就执行完毕,因此是优先于构造器和普通代码块执行的
- 创建一个子类时,他们的静态代码块,静态属性初始化,普通代码块,普通属性初始化,构造方法的调用顺序如下:
-
- 父类的静态代码块和静态属性(优先级一样,按定义顺序执行)
- 子类的静态代码块和静态属性(优先级一样,按定义顺序执行)
- 父类的普通代码块和普通属性初始化(优先级一样,按定义顺序执行)
- 父类的构造方法
- 子类的普通代码块和普通属性初始化(优先级一样,按定义顺序执行)
- 子类的构造方法
- 静态代码块只能直接调用静态成员(静态属性和静态方法),普通代码块可以调用任意代码。
//static代码块是类加载时,执行,只会执行一次
//普通代码块是在创建对象时调用,创建一次,调用一次
public class CodeDetail {
public static void main(String[] args) {
System.out.println(Teach.classroom);//使用类的静态属性
单独执行输出//
//父类静态代码块
//父类静态属性
System.out.println("-----------------");
Teach t = new Teach("李四");//创建对象实例
单独执行输出/
//父类静态代码块
//父类非静态代码块
//父类的构造器
System.out.println("-----------------");
Talk tl = new Talk("张三");//创建子类对象实例(1)类加载(2)对象创建
单独执行输出
//父类静态代码块
//子类静态代码块
//父类非静态代码块
//父类的构造器
//子类非静态代码块
//子类的构造器
System.out.println("-----------------");
System.out.print(Talk.word);//使用类的静态属性
///单独执行输出///
//父类静态代码块
//子类静态代码块
//子类静态属性
/四句一起执行输出
//父类静态代码块
//父类静态属性
//---------------------
//父类非静态代码块
//父类的构造器
//---------------------
//子类静态代码块
//父类非静态代码块
//父类的构造器
//子类非静态代码块
//子类的构造器
//----------------------
//子类静态属性
}
}
class Teach{
private String name;
private String object;
public static String classroom = "父类静态属性";
static{
System.out.println("父类静态代码块");
}
{
System.out.println("父类非静态代码块");
}
public Teach(String name) {
this.name = name;
System.out.println("父类的构造器");
}
}
class Talk extends Teach{
public static String word = "子类静态属性";
static{
System.out.println("子类静态代码块");
}
{
System.out.println("子类非静态代码块");
}
public Talk(String name) {
super(name);
System.out.println("子类的构造器");
}
}
//输出结果:
//getVal01
//A02 的一个静态代码块..
//getVal03
//B02 的一个静态代码块..
//A02 的第一个普通代码块..
//getVal02
//A02 的构造器
//getVal04
//B02 的第一个普通代码块..
//B02 的构造器
public class CodeBlockDetail04 {
public static void main(String[] args) {
new B02();
}
}
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 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)
}
}
单例设计模式
设计模式
- 静态方法和属性的经典使用
- 设计模式是在大量的实践中总结和理论化之后优选的代码结构、编程风格、以及解决问题的思考方式,设计模式就像是经典的棋谱,不同的棋局,我们用不同的棋谱,免得我们自己再思考和摸索。
- 单例设计模式就是采取一定的方法保证在整个的软件系统中,对某个类只能存在一个对象实例,并且该类只提供一个取的其对象实例的方法。
饿汉式
- 构造器私有化
- 类的内部创建对象
- 向外暴露一个静态的公共方法
- 饿汉体现在在不确定会不会用到这个对象实例的时候,这个对象就已经创建好了,因为对象的创建是static的,会在类加载的时候对象就会创建,但如果对象是重量级的,就有可能造成资源的浪费。
public class HungryMan {
public static void main(String[] args) {
GirlFriend instance = GirlFriend.getGirlFriend();
System.out.println(instance);
GirlFriend onceAgain = GirlFriend.getGirlFriend();
System.out.println(onceAgain);
System.out.println(instance == onceAgain);
}
}
class GirlFriend{
private String name;
//为了能够将girlFriend在下面静态方法中使用,所以其为静态
private static GirlFriend girlFriend = new GirlFriend("小薇");
private GirlFriend(String name) {
this.name = name;
}
public static GirlFriend getGirlFriend(){
return girlFriend;
}
@Override
public String toString() {
return "GirlFriend{" +
"name='" + name + '\'' +
'}';
}
}
懒汉式
public class LazyMan {
public static void main(String[] args) {
System.out.println(Boyfriend.getBoyfriend());
}
}
class Boyfriend{
private String name;
private static Boyfriend boyfriend;
private Boyfriend(String name){
this.name = name;
}
public static Boyfriend getBoyfriend(){
if(boyfriend == null){
boyfriend = new Boyfriend("菠萝吹雪");
}
return boyfriend;
}
@Override
public String toString() {
return "Boyfriend{" +
"name='" + name + '\'' +
'}';
}
}
总结
- 二者最主要的区别在于创建对象的时机不同:饿汉式是在类加载就创建了对象实例,而懒汉式是在使用时才创建;
- 饿汉式不存在线程安全问题,懒汉式存在线程安全问题;
- 饿汉式存在浪费资源的可能,因为如果程序员一个对象实例都没有使用,那么饿汉式创建的对象就浪费了,懒汉式是使用时才创建,就不存在这个问题;
- JavaSE标准类中,java.lang.Runtime就是经典的单例模式。
final 关键字
基本介绍
- final可以修饰类、属性、方法和局部变量,以下方法可能会用到:
- 当不希望类被继承时,就可以用final修饰类
- 当不希望父类的某个方法被子类覆盖/重写,就可以用final修饰方法
- 当不希望类的某个属性的值被修改,就可以用final修饰属性
- 当不希望某个局部变量被修改,就可以用final修饰局部变量
细节
- final修饰的属性又叫常量,一般用AA_AA命名
- final修饰的属性在定义时必须给值,之后不能修改,赋值可以在如下位置之一:
-
- 定义时
- 在构造器中
- 在代码块中
- 如果final修饰的属性是静态的,则初始化的位置只能
-
- 定义时
- 在静态代码块中,不能在构造器中赋值
- final类不能继承,但是可以实例对象
- 如果类不是final类,但是含有final方法,则该方法不能重写,但是可以被继承
- 一般来说,如果一个类已经是final类了,就没必要将其中方法修饰为final方法
- final不能修饰构造方法(构造器)
- final和static往往搭配使用,效率更高,不会导致类加载,底层编译器做了优化处理
public class Test{
public static void main(String[] args){
System.out.println(BBB.num);//只会输出10000,类并没有被加载
}
}
class BBB{
public final static int num = 10000;
static{
System.out.println("BBB 静态代码块");
}
}
- 包装类(Integer,Double,Float,Boolean等都是final),String也是final类
class Something{
public int addOne(final int x){//允许
// ++x; //不允许,不允许修改x的值
return x + 1;
}
}
抽象类
介绍
当父类的某些方法,需要声明,但是又不确定如何实现时,可以将其声明为抽象方法,那么这个类就是抽象类。
- 当父类的一些方法不能确定时,可以用abstract关键字来修饰该方法,这个方法就是抽象方法,用abstract来修饰该类就是抽象类。
public abstract class Animal{
String name;
int age;
public abstract public void cry();
}
- 一般来说,抽象类会被继承,有其子类来实现抽象方法。
细节
- 抽象类不能实例化
- 抽象类不一定要包含abstract方法,也就是说,抽象类可以没有abstract方法,而且可以有实际的方法
- 一旦类包含了abstract方法,则这个类必须声明为abstract
- abstract只能修饰类和方法,不能修饰属性和其他的
- 抽象类可以有任意成员【抽象类还是类】,比如非抽象方法、构造器、抽象方法、静态方法、属性、静态属性等
- 抽象方法不能有主体,既不能实现
- 如果一个类继承了抽象类,则它必须实现抽象类的所有抽象方法【所谓实现也就是有方法体{},方法体{}中可以没有内容】,除非它自己也声明为abstract类
- 抽象方法不能使用private、final和static来修饰,因为这些关键字都是和重写相违背的
模板设计模式
- 有多个类,完成不同的任务job
- 要求能够得到各自完成任务的时间
具体实现
- 编写方法caleTime(),可以计算某段代码的耗时时间
- 编写抽象方法code()
- 编写一个子类Sub,继承抽象类Template,并实现code方法
- 编写测试类
public abstract class Template {
public abstract void code();
public void caleTime(){
long start = System.currentTimeMillis();
code();
long end = System.currentTimeMillis();
System.out.println("执行时间" + (end - start));
}
}
public class AA extends Template{
public void code(){
long num = 0;
for (int i = 1; i <= 1000000; i++) {
num += i;
}
}
}
public class BB extends Template{
public void code(){
long num = 0;
for (int i = 1; i <= 1000000000; i++) {
num += i;
}
}
}
public class TestTemplate {
public static void main(String[] args) {
AA aa = new AA();
aa.caleTime();
BB bb = new BB();
bb.caleTime();
}
}
接口
介绍
接口就是给出一些没有实现的方法,封装到一起,到某个类要使用的时候,在根据具体情况把这些方法写出来。
interface 接口名{
//属性
//方法 在接口中抽象方法可以省略abstract关键字
}
class 类名 implements 接口{//实现接口,将实现接口中所有的抽象方法
自己属性;
自己方法;(1.抽象方法 2.默认实现方法 3.静态方法)
必须实现的接口的抽象方法
}
小结:
1.在jdk 7.0前接口里的所有方法都没有方法体
2.jdk 8.0后接口类可以有静态方法、默认方法,也就是说接口中可以有方法的具体实现,需要使用default关键字修饰,也可以有静态方法,即用static修饰。
细节
- 接口不能被实例化
- 接口中所有的方法是 public 方法,接口中抽象方法,可以不用 abstract修饰
- 一个普通类实现接口,就必须将该接口的所有方法都实现
- 抽象类实现接口,可以不用实现接口的方法
- 一个类同时可以实现多个接口 class 类名 implements 接口1,接口2··· ···
- 接口中的属性,只能是final的,而且是public static final修饰符
- 接口中属性的访问形式:接口名.属性名
- 一个接口不能继承其他的类,但是可以继承多个别的接口 interface A extends B,C{}
- 接口的修饰符只能是public和默认,这点和类的修饰符是一样的
interface A{
int a = 23;//public static final int a = 23;
}
class B implements A{
}
B b = new B();//ok
System.out.println(b.a);//23
System.out.println(A.a);//23
System.out.println(B.a);//23
继承和接口
- 接口和继承解决的问题不同
-
- 继承的价值主要在于:解决代码的复用性和可维护性
- 接口的价值主要在于:设计,设计好各种规范(方法),让其他类去实现这些方法。
- 接口比继承更加灵活
-
- 接口比继承更加灵活,继承是满足is-a的关系,而接口只需满足like - a的关系。
- 接口在一定程度上实现代码解耦【接口规范性+动态绑定机制】
public class ExtendsVsInterface {
public static void main(String[] args) {
LittleMonkey wuKong = new LittleMonkey("悟空");
wuKong.climbing();
wuKong.swimming();
wuKong.flying();
}
}
//猴子
class Monkey {
private String name;
public Monkey(String name) {
this.name = name;
}
public void climbing() {
System.out.println(name + " 会爬树...");
}
public String getName() {
return name;
}
}
//接口
interface Fishable {
void swimming();
}
interface Birdable {
void flying();
}
//继承
//小结: 当子类继承了父类,就自动的拥有父类的功能
// 如果子类需要扩展功能,可以通过实现接口的方式扩展.
// 可以理解 实现接口 是 对java 单继承机制的一种补充.
class LittleMonkey extends Monkey implements Fishable,Birdable {
public LittleMonkey(String name) {
super(name);
}
@Override
public void swimming() {
System.out.println(getName() + " 通过学习,可以像鱼儿一样游泳...");
}
@Override
public void flying() {
System.out.println(getName() + " 通过学习,可以像鸟儿一样飞翔...");
}
}
接口多态
- 多态参数
-
- 接口引用可以指向实现了接口的类的对象
- 多态数组
- 接口存在多态传递现象
public class InterfacePolyArr {
public static void main(String[] args) {
//多态数组 -> 接口类型数组
Usb[] usbs = new Usb[2];
usbs[0] = new Phone_();
usbs[1] = new Camera_();
/*
给Usb数组中,存放 Phone 和 相机对象,Phone类还有一个特有的方法call(),
请遍历Usb数组,如果是Phone对象,除了调用Usb 接口定义的方法外,
还需要调用Phone 特有方法 call
*/
for(int i = 0; i < usbs.length; i++) {
usbs[i].work();//动态绑定..
//和前面一样,我们仍然需要进行类型的向下转型
if(usbs[i] instanceof Phone_) {//判断他的运行类型是 Phone_
((Phone_) usbs[i]).call();
}
}
}
}
interface Usb{
void work();
}
class Phone_ implements Usb {
public void call() {
System.out.println("手机可以打电话...");
}
@Override
public void work() {
System.out.println("手机工作中...");
}
}
class Camera_ implements Usb {
@Override
public void work() {
System.out.println("相机工作中...");
}
}
public class InterfacePolyPass {
public static void main(String[] args) {
//接口类型的变量可以指向,实现了该接口的类的对象实例
IG ig = new Teacher();
//如果IG 继承了 IH 接口,而Teacher 类实现了 IG接口
//那么,实际上就相当于 Teacher 类也实现了 IH接口.
//这就是所谓的 接口多态传递现象.
IH ih = new Teacher();
}
}
interface IH {
void hi();
}
interface IG extends IH{ }
class Teacher implements IG {
@Override
public void hi() {
}
}
interface A{
int x = 0;//public final static int x = 0
}
class B{
int x = 1;
}
class C extends B implements A{
public void pX(){
System.out.println(A.x + " " + super.x);//明确指定x
}
public static void main(String[] args){
new C().pX();
}
}
内部类
一个类的内部又完整的嵌套了另一个类结构,被嵌套的类称为内部类(inner class)嵌套其他类的类称为外部类(outer class),是类的第五大成员【属性、方法、构造器、代码块、内部类】,内部类最大的特点就是可以直接访问私有属性,并且可以体现类与类之间的包含关系。
class Outer{
class Inner{
}
}
class Other{
}
分类
- 定义在外部类局部位置上(比如方法内)
-
- 局部内部类(有类名)
- 匿名内部类(没有类名,重点!!!!!!!!)
- 定义在外部类的成员位置上
-
- 成员内部类(没用static修饰)
- 静态内部类(使用static修饰)
局部内部类
局部内部类是定义在外部类的局部位置,比如方法/代码块中,并且有类名。
- 可以直接访问外部类的所有成员,包括私有
- 不能添加访问修饰符,因为它的地位就是一个局部变量,局部变量是不能使用修饰符的,但是可以使用final修饰,因为局部变量也可以使用final
- 作用域:仅仅在定义它的方法或代码块中
- 局部内部类—访问—外部类的成员【访问方式:直接访问】
- 外部类—访问—局部内部类的成员【访问方式:创建对象,再访问(必须在作用域内)】内部类本质仍然是一个类
- 外部其他类----不能访问----局部内部类(因为局部内部类地位是一个局部变量)
- 如果外部类和局部内部类的成员重名时,默认遵循就近原则,如果想访问外部类的成员,则可以使用(外部类名.this.成员)去访问
- 外部类名.this 本质就是外部类的对象,即哪个对象调用了内部类所在的方法,就输出哪个外部类对象的属性
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();
}
}
匿名内部类
匿名内部类是定义在外部类的局部位置,比如说方法体、代码块中,并且没有类名。
(1)本质是类(2)内部类(3)该类没有名字(系统分配)(4)同时还是一个对象
new 类或接口(参数列表){
类体
};
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();
}
细节
- 匿名内部类的语法比较奇特,因为匿名内部类既是一个类的定义,同时它本身也是一个对象,因此从语法上看,它既有定义类的特征,也有创建对象的特征,因此可以调用匿名内部类方法。
- 可以直接访问外部类的所有成员,包括私有的
- 不能添加访问修饰符,因为它的地位就是一个局部变量
- 作用域:仅仅在定义它的方法或代码块中
- 匿名内部类-----访问------外部类成员【访问方式:直接访问】
- 外部其他类----不能访问----匿名内部类(因为匿名内部类地位是一个局部变量)
- 如果外部类和匿名内部类的成员重名时,默认遵循就近原则,如果想访问外部类的成员,则可以使用(外部类名.this.成员)去访问
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);
}
}
//抽象类/接口...
实践
- 当作实参直接传递
public class InnerClassExercise01 {
public static void main(String[] args) {
//当做实参直接传递,简洁高效
f1(new IL() {
@Override
public void show() {
System.out.println("这是一副名画~~...");
}
});
//传统方法
f1(new Picture());
}
//静态方法,形参是接口类型
public static void f1(IL il) {
il.show();
}
}
//接口
interface IL {
void show();
}
//类->实现IL => 编程领域 (硬编码)
class Picture implements IL {
@Override
public void show() {
System.out.println("这是一副名画XX...");
}
}
///
// 练习
///
interface Bell{
void ring();
}
class Cellphone{
public void alarmclock(Bell b){
b.ring();
}
}
public class Test{
public static void main(String[] args){
Cellphone c = new Cellphone();
c.alarmclock(new Bell(){
public void ring(){
System.out.println("懒猪起床啦");
}
}
);
c.alarmclock(new Bell(){
public void ring(){
System.out.println("小伙伴上课了");
}
}
);
}
}
成员内部类
定义在外部类的成员位置上,并且没有static修饰。
- 可以直接访问外部类的所有成员,包含私有的
- 可以添加任意访问修饰符(public、protected、默认、private),因为它的地位就是一个成员
- 作用域:和外部类的其他成员一样,为整个类体
- 成员内部类----访问----外部类(比如:属性)【访问方式:直接访问】
- 外部类—访问----内部类【访问方式:创建对象,再访问】
- 外部其他类----访问-----成员内部类
- 如果外部类和内部类的成员重名时,内部类访问的话,默认遵循就近原则,如果想访问外部类的成员,则可以使用(外部类名.this.成员)去访问
//第一种方法:在main函数中
Outer outer = new Outer();
Outer.Inner inner = outer.new Inner();
//相当于把 new Inner()当作是outer的一个成员
//------------------------------------------------------
//第二种方法:在Outer类中写一个方法返回一个Inner对象
Outer outer = new Outer();
Outer.Inner inner = outer.getInnerInstance();
public Inner getInnerInstance(){
return new Inner();
}
//------------------------------------------------------
//第三种方法:将第一种方式合二为一
Outer.Inner inner = new Outer().new Inner();
静态内部类
定义在外部类的成员位置上,有static修饰。
- 可以直接访问外部类的所有静态成员,包含私有的,但不能直接访问非静态成员
- 可以添加任意访问修饰符(public、protected、默认、private),因为它的地位就是一个成员
- 作用域:和外部类的其他成员一样,为整个类体
- 静态内部类----访问----外部类(比如:静态属性)【访问方式:直接访问】
- 外部类—访问----静态内部类【访问方式:创建对象,再访问】
- 外部其他类----访问-----静态内部类
- 如果外部类和内部类的成员重名时,内部类访问的话,默认遵循就近原则,如果想访问外部类的成员,则可以使用(外部类名.成员)去访问
//第一种方法:在main函数中
Outer outer = new Outer();
Outer.Inner inner = new Outer.Inner();
//因为静态内部类是可以通过类名直接访问(满足访问权限)
//------------------------------------------------------
//第二种方法:在Outer类中写一个方法返回一个Inner对象
Outer outer = new Outer();
Outer.Inner inner = outer.getInnerInstance();
public Inner getInnerInstance(){
return new Inner();
}
//------------------------------------------------------
//第三种方法:在Outer类中写一个静态方法返回一个Inner对象
Outer.Inner inner = Outer.getInnerInstance();
public static Inner getInnerInstance(){
return new Inner();
}