代码块,内部类
代码块1:代码块的基本介绍和基本语法2:体会代码块的用处3:代码块的细节4:创建一个对象时,在一个类中的调用顺序(难点) 单例设计模式1:介绍单例模式2:单例模式应用的实现1:饿汉式2:懒汉式 3:小结 内部类1:内部类的分类2:内部类的基本语法2.1:局部内部类2.1.1基本语法 2.2:匿名内部类2.2.1:基本语法2.2.2:最佳用法 3.1:成员内部类3.1.1:基本语法 4:静态内部类的使用4.1:基本语法
代码块
1:代码块的基本介绍和基本语法
代码块又被称为初始块,属于类中的成员,类似于方法把逻辑语句放在{}中,但是没有名字返回值,没有参数,也不能被调用,在加载类或者创建对象的时候被隐式调用。语法:[修饰符] {代码};
修饰符可选且只能是static代码块分两种一种是静态代码块,一种是实例代码块/普通代码/非静态代码块;可以写上也可以省略 2:体会代码块的用处
相当于另外一种形式的构造体,可以进行初始化此操作当多个构造器中都有重复的语句,就可以抽取到初始代码块中,提高代码重用性class CodeBlock01 { public static void main(String[] args) { Movie movie = new Movie("你好,李焕英"); System.out.println("==============="); Movie movie2 = new Movie("唐探 3", 100, "陈思诚"); }}class Movie { private String name; private double price; private String director;//3 个构造器-》重载//老韩解读//(1) 下面的三个构造器都有相同的语句//(2) 这样代码看起来比较冗余//(3) 这时我们可以把相同的语句,放入到一个代码块中,即可//(4) 这样当我们不管调用哪个构造器,创建对象,都会先调用代码块的内容//(5) 代码块调用的顺序优先于构造器.. {System.out.println("电影屏幕打开...");System.out.println("广告开始...");System.out.println("电影正是开始...");};public Movie(String name) { System.out.println("Movie(String name) 被调用..."); this.name = name; }public Movie(String name, double price) { this.name = name; this.price = price; }public Movie(String name, double price, String director) { System.out.println("Movie(String name, double price, String director) 被调用..."); this.name = name; this.price = price; this.director = director; }}
3:代码块的细节
static代码块也叫静态代码块,作用是对类进行初始化,而且它之随着类的加载而执行,且只会执行一次,如果是普通代块,每创建一个对象,执行一次类加载的几种情况:创建对象实例的时候,创建子对象的实例,父类类也会被加载,使用静态成员时(静态属性,静态方法)普通代码块:在创建对象实例的时候,会被隐形调用,被创建一次就会调用一次,如果使用类的静态成员,普通代码块并不会执行。static的=代码是类加载的时候,且只会被执行一次,普通代码块是创建对象的时候,创建一次对象执行一次public class Code { 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}}
4:创建一个对象时,在一个类中的调用顺序(难点)
调用静态代码块和静态属性初始化:静态代码块和静态属性的调用优先级一样,如果有多个代码块和多个静态变量初始化,则按他们的顺序调用调用普通代码块和普通属性的初始化:两者调用的优先级一样,如果有多个普通代码块和多个普通属性初始化,则按他们定义的顺序来调用class A { { //普通代码块 System.out.println("A 普通代码块 01"); } private int n2 = getN2();//普通属性的初始化 static { //静态代码块 System.out.println("A 静态代码块 01"); } //静态属性的初始化 private static int n1 = getN1(); public static int getN1() { System.out.println("getN1 被调用..."); return 100; } public int getN2() { //普通方法/非静态方法 System.out.println("getN2 被调用..."); return 200; } //无参构造器 public A() { System.out.println("A() 构造器被调用"); }}
构造器的最前面其实隐含了super()和调用普通代码块,静态代码块和静态属性初始化,在类的加载时候,就执行完成,因此优于构造器和普通代码块执行的 class A {public A() {//构造器//这里有隐藏的执行要求//(1)super();// (2)调用普通代码块System.out.println("ok");}}
public class Code{ public static void main(String[] args) { new BBB();//(1)AAA 的普通代码块(2)AAA() 构造器被调用(3)BBB 的普通代码块(4)BBB() 构造器被调用 }}class AAA { //父类 Object { System.out.println("AAA 的普通代码块"); } public AAA() {//(1)super()//(2)调用本类的普通代码块 System.out.println("AAA() 构造器被调用...."); }}class BBB extends AAA { { System.out.println("BBB 的普通代码块..."); } public BBB() {//(1)super()//(2)调用本类的普通代码块 System.out.println("BBB() 构造器被调用...."); }}
总结:创建一个子类父类对象时候(继承关系),他们的静态代码,静态属性初始化,普通代码块,普通属性初始化,构造方法的调用顺序如下:1:父类的静态方法和的静态属性(优先级一样,按定义的顺序)2:子类的静态代码块和静态属性(优先级一样,按定义的顺序)3:父类的普通代码块和普通属性初始化(优先级一样,按定义的顺序执行)4:父类的构造方法5:子类的普通代码块和普通属性初始化(优先级一样,按定义的顺序执行)6:子类的构造方法 class Sample{ Sample(String s) { System.out.println(s); } Sample() { System.out.println("Sample 默认构造函数被调用"); }}//====public class Test{ Sample sam1=new Sample("sam1 成员初始化");// static Sample sam=new Sample("静态成员 sam 初始化 ");// static{ System.out.println("static 块执行");// if(sam==null)System.out.println("sam is null"); } Test()//构造器 { System.out.println("Test 默认构造函数被调用");// } //主方法 public static void main(String str[]) { Test a=new Test();//无参构造器 }}//运行结果, 输出什么内容,并写出. 2min 看看//1. 静态成员 sam 初始化// 2. static 块执行// 3. sam1 成员初始化// 4. Test 默认构造函数被调用
单例设计模式
1:介绍单例模式
所谓的单机设计模式,就是采用一定的方法保证在整个软件系统中,对某个类只存在一个对象实例,并且该类只提供一个取得对象实例的方法单例模式有两种方法:饿汉式和懒汉式2:单例模式应用的实现
public class Test { public static void main(String[] args) { GirlFriend xh = new GirlFriend("小红"); GirlFriend xb = new GirlFriend("小白"); }}class GirlFriend { private String name; public GirlFriend(String name) { this.name = name; }}问题抛出,我们只要有一个女朋友对象,怎么去实现呢?
创建方法:构造器私有化(防止直接new),类的内部创建对象,向外暴露一个静态公共方法。 1:饿汉式
public class Test { public static void main(String[] args) { GirlFriend instance1 = GirlFriend.getInstance(); System.out.println(instance1); GirlFriend instance2 = GirlFriend.getInstance(); System.out.println(instance2); System.out.println(instance1 == instance2); System.out.println(GirlFriend.n1); }}class GirlFriend { public static int n1 = 100; private String name; //第一步将构造器私有化 private GirlFriend(String name) { System.out.println("构造方法被调用。。。"); this.name = name; } //第二部在类的内部创建一个静态的对象 private static GirlFriend gf = new GirlFriend("小红"); //第三步提提供一个方法,返回一个对象的引用 public static GirlFriend getInstance() { return gf; } @Override public String toString() { return "GirlFriend{" + "name='" + name + " " +"age:"+n1+""+ '}'; }}
饿汉式的缺点:饿汉式,一般创造重量级的对象,但是有可能创建了对象没有使用,造成资源的浪费
2:懒汉式
class Cat{ public static int n1 = 999; private String name; private static Cat cat; //构造器私有化 private Cat(String name) { this.name = name; System.out.println("构造方法被调用"); } //提供一个静态的方法,返回一个对象的引用 public static Cat getInstance() { if(cat==null) { //创建一个对象 cat = new Cat("小白"); } return cat; } @Override public String toString() { return "Cat{" + "name='" + name + '\'' + '}'; }}public class Test { public static void main(String[] args) {// Cat cat1 = Cat.getInstance();// System.out.println(cat1);// Cat cat2 = Cat.getInstance();// System.out.println(cat2);// System.out.println(cat1 == cat2); System.out.println(Cat.n1); }}
懒汉式缺陷:会存在线程安全问题,当多个线程同时调用getInstance()时候,会产生线程安全问题。
3:小结
二者的区别在于创建对象的时机不同,饿汉式在类加载时候创建实例对象,懒汉式是在使用时候才创建饿汉式不存在线程问题,懒汉式存在线程安全问题饿汉式存在浪费资源的可能(程序员创建后不使用),懒汉式不存在浪费资源的问题在我们javaSE标准类中,java.lang.Runtime就是经典的单例模式内部类
前言:内部类是在一个类的内部又听到的另一个类的结构,被嵌套的类成为内部类,嵌套其他类的对象成为外部类1:内部类的分类
按位置可以分为两类:在属性位置上和在方法里面的内部类定义在类的局部位置(方法/代码块)(1)局部内部类(有类名),(2)匿名内部类(没有类名)定义在成员位置,(1)成员内部类(没有static修饰),(2)静态内部类(有static修饰)2:内部类的基本语法
2.1:局部内部类
2.1.1基本语法
说明:局部内部类是定义在外部类的局部位置,比如方法中(方法块中),并且有类名1:可以直接防蚊外部类的成员和方法,包括私有的2:不能添加访问修饰符,因为它的地位就是一个局部变量,局部变量是不能使用修饰符的,但是可以使用final和abstract修饰,因为局部变量也可以使用final3:作用域仅仅是它的方法体内或者是代码块内4:局部内部类----访问------->外部类成员【直接访问】5:外部类----访问----->局部内部类的成员,访问方式:创建对象,在访问(必须在作用域内)6:外部其他类-----不能访问----->局部内部类(因为局部内部类的地位是一个局部变量)6:如果外部类和局部内部类的成员重名时,默认遵循就近原则,如果访问外部类的成员,则可以使用(外部类名.this.成员)记住:(局部内部类定义在方法中/代码块),作用域在方法体或者代码块,本质还是一个类class Outer{ //外部类方法{class Inner//}}
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(); }}
2.2:匿名内部类
2.2.1:基本语法
1:本质还是类,内部类,该类没有名字,同时还是一个对象2:匿名内部类定义在外部类的局部位置,比如方法和方法体3:内部类的基本语法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$1class Outer04$1 implements IA {@Overridepublic 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{@Overridepublic 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();}
2:匿名类的语法比较特殊,匿名类即使一个类的定义,也是一个对象,从语法层面上看,它既有定义类的特征,也有创建对象的特征,因此可以调用匿名内部类的方法3:可以直接访问外部所有类的成员,包括私有的4:不能添加访问修饰符,因为它的地位是一个局部变量5:作用域:仅仅是它这个方法内,或者代码块内6:匿名内部类-----访问-----外部类成员(直接访问)7:外部其他类不能访问匿名内部类(因为匿名内部类是一个局部变量)8:如果外部类和匿名内部类的成员重名时,匿名内部类访问,,默认执行就近原则,想要访问外部类成员,则可以使用(外部类名.this.成员)去访问 public class AnonymousInnerClass { 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); }}//抽象类/接口...
2.2.2:最佳用法
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..."); }}
3.1:成员内部类
3.1.1:基本语法
说明:成员内部类是定义在外部类的成员位置,并且没有static修饰1:可以直接访问外部类的所有成员,包括私有的2:可以添加任意的访问修饰符,因为它的地位就是一个成员3:作用域:和外部类的其他成员一样,为整个类体4:成员内部类-----访问----->外部类成员(直接访问)、5:外部类------访问----->成员内部类(先创造对象,访问)6:外部其他类-----访问-----成员内部类(创造外部类对象)7:如果外部类和内部类成员重名时,内部类访问的话,默认遵循就近原则,如果像访问外部类的成员,则可以使用(外部类.this.成员)去访问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); }}
4:静态内部类的使用
4.1:基本语法
说明静态内部类是定义在外部类的成员位置,并且有static修饰1:可以最直接访问外部类所有静态成员,包含私有的,但不能直接访问非静态成员2:可以添加任意访问修饰符,因为它的地位是一个成员3:作用域:同其他成员,是整个类体4:静态内部类----访问---->外部类(比如静态属性)(直接访问所有的静态属性成员)5:外部类—访问------静态内部类 访问方式:创建对象,再访问6:其他外部其他类—访问—静态内部类7:如果外部类和静态内部类的成员重名时,静态内部类访问时,默认遵循就近原则,如果访问外部类成员,则可以使用(类名,成员)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(); }}