说:有一群小孩在玩堆雪人,不时有新的小孩加入,请问如何知道现在共有多少人在玩?
传统的方式解决
思路
在main方法中定义一个变量count
当一个小孩加入游戏后count++,最后个count就记录有多少个小孩玩游戏
package com.lyn.static_; /** * @author L.yn * @version 1.0 * @date 2021/12/25 13:46 */ public class ChildGame { public static void main(String[] args) { //定义一个变量count,统计有多少个小孩加入了游戏 int count = 0; Child a = new Child("a"); a.join(); count++; Child b = new Child("b"); b.join(); count++; Child c = new Child("c"); c.join(); count++; } } class Child { private String name; public Child(String name) { this.name = name; } public void join() { System.out.println(name + "加入游戏"); } }
问题分析
类变量快速入门
思考:如果,设计一个int count表示总人数,我们在创建一个小孩时,就把count加1,并且count是所有对象共享的就ok了!
package com.lyn.static_; /** * @author L.yn * @version 1.0 * @date 2021/12/25 13:46 */ public class ChildGame { public static void main(String[] args) { //定义一个变量count,统计有多少个小孩加入了游戏 Child a = new Child("a"); a.join(); a.count++; Child b = new Child("b"); b.join(); b.count++; Child c = new Child("c"); c.join(); c.count++; } } class Child { private String name; //定义一个变量count,是一个类变量(静态变量)static静态 //该变量最大的特点就是会被child类所有对象共享 public static int count = 0; public Child(String name) { this.name = name; } public void join() { System.out.println(name + "加入游戏"); } }
不管static变量在哪里,共识
什么是类变量
如何定义类变量
如何访问类变量
类名.类变量名
对象名.类变量名【静态变量的访问修饰符的访问权限和范围和普通属性是一样的】
推荐使用:类名.类变量名;
package com.lyn.static_; /** * @author L.yn * @version 1.0 * @date 2021/12/25 14:19 */ public class VisitStatic { public static void main(String[] args) { //类名.类变量名 //说明:类变量是随着类的加载而创建,所以即使没有创建对象实例,也可以访问 System.out.println(A.name);//推荐 //对象名.类变量名 A a = new A(); System.out.println(a.name); } } class A { //类变量 //类变量的访问,必须遵守相关的访问权限。 public static String name = "xixi"; }
类变量使用注意事项和细节讨论
类方法基本介绍
类方法也叫静态方法
形式如下:
访问修饰符 static 数据返回类型 方法名(){}【推荐】
static 访问修饰符 数据返回类型 方法名(){}
类方法的调用
使用方式:类名.类方法名 或者对象名.类方法名【前提:满足访问修饰符的访问权限和范围】
package com.lyn.static_; /** * @author L.yn * @version 1.0 * @date 2021/12/25 14:34 */ public class StaticMethod { public static void main(String[] args) { //创建两个学生对象,交学费 Stu tom = new Stu("tom"); tom.payFee(100); // Stu.payFee(100);//对不对?对 Stu mary = new Stu("mary"); tom.payFee(200); //输出当前收到的总学费 Stu.showFee();//总学费有:300.0 } } class Stu { private String name;//普通成员 //定义一个静态变量,来累计学生的学费 private static double fee = 0; public Stu(String name) { this.name = name; } /** * 说明: * 一、当方法使用static修饰后,该方法就是静态方法 * 二、静态方法就可以访问静态属性/变量 * * @param fee */ public static void payFee(double fee) { Stu.fee += fee; } public static void showFee() { System.out.println("总学费有:" + Stu.fee); } }
类方法经典使用场景
小结
类方法使用注意事项和细节讨论
深入理解main方法
解释main方法的形式:public static void main(String[] args){}
特别提示:
在main方法中,我们可以直接调用main方法所在类的静态方法或静态属性
但是,不能直接访问该类中的非静态成员,必须创建该类的一个实例对象后,才能通过这个对象去访问类中的非静态成员
package com.lyn.main_; /** * @author L.yn * @version 1.0 * @date 2021/12/25 15:28 */ public class Main01 { //静态的变量/属性 private static String name = "xixi"; //非静态的变量/属性 private int n1 = 100; public static void hi() { System.out.println("Main01的hi方法"); } public void cry() { } public static void main(String[] args) { //可以直接使用name /** * 一、静态方法可以访问本类的静态成员 * 二、静态方法main不可以访问本类的非静态成员 * 三、静态方法main要访问本类的非静态成员,需要先创建对象,在调用即可 */ System.out.println(name); hi(); // System.out.println(n1);//错误 // cry();//错误 Main01 main01 = new Main01(); System.out.println(main01.n1); main01.cry(); } }
在idea中如何传递参数
代码块又称为代码化块,属于类中的成员【即是类的一部分】,类似于方法,讲逻辑语句封装在方法体中,通过{}包围起来
但和方法不同,没有方法名,没有返回,没有参数,只有方法体,而且不通过对象或类显示调用,而是加载类时,或创建对象时隐式调用
【修饰符】{
代码
}
注意
代码块的好处和案例演示
相当于另一种形式的构造器(对构造器的补充机制),可以做初始化的操作
如果多个构造器中都有重复的语句,可以抽取到初始化块中,提高代码的重用性
代码块的快速入门
package com.lyn.codeblock_; /** * @author L.yn * @version 1.0 * @date 2021/12/25 15:47 */ public class Codeblock01 { public static void main(String[] args) { Movie hello = new Movie("hello"); } } 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("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) { this.name = name; this.price = price; this.director = director; } }
代码块使用注意事项和细节讨论
static代码块也叫静态代码块,作用就是对类进行初始化,而且它随着类的加载而执行,并且指挥执行一次,如果是普通代码块,每创建一个对象,就执行
类什么时候被加载【重要!】
普通代码块,在创建对象实例时,会被隐式调用,被创建一次,就会调用一次,如果只是使用类的静态成员时,普通代码块并不会执行
package com.lyn.codeblock_; /** * @author L.yn * @version 1.0 * @date 2021/12/25 20:00 */ public class CodeBlockDetail01 { public static void main(String[] args) { //类被加载的情况举例 //static代码块,是在类加载时执行,只会执行一次 //一、创建对象实例时(new) AA aa1 = new AA(); //二、创建子类对象实例,父类也会被加载,而且父类先被加载,子类其次 AA aa2 = new AA(); //三、使用类的静态成员时(静态属性,静态方法) System.out.println(Cat.n1); Cat.notice(); } } class Animal { static { System.out.println("Animal的静态代码块1被执行..."); } } class Cat extends Animal { public static int n1 = 999; public static void notice() { System.out.println("notice被执行..."); } 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 } }
创建一个对象时,在一个类调用顺序【重点,难点】
我们看一下创建一个子类时(继承关系),他们的静态代码块,静态属性初始化,普通代码块,普通属性初始化,构造方法的调用顺序如下
静态代码块只能直接调用静态成员(静态属性和静态方法),普通代码块可以调用任意成员
什么是设计模式
什么是单例模式(单个的实例)
所谓类的单例设计模式,就是采取一定的方法保证在整个的软件系统中,对某个类只能存在一个对象实例,并且该类只提供一个取得其对象实例的方法
单例模式有两种方式
饿汉式
package com.lyn.single_; /** * @author L.yn * @version 1.0 * @date 2021/12/25 20:26 */ public class SingleTon01 { public static void main(String[] args) { // GirlFriend xh = new GirlFriend("xh"); // GirlFriend xb = new GirlFriend("xb");、 //通过方法可以获取对象 GirlFriend instance = GirlFriend.getInstance(); System.out.println(instance.toString()); GirlFriend instance1 = GirlFriend.getInstance(); System.out.println(instance1.toString()); System.out.println(instance == instance1);//true } } /** * 有一个类:GirlFriend * 只能有一个女朋友 */ class GirlFriend { private String name; //为了能够在静态方法中使用,返回一个gf对象,需要将其修饰为static //对象,通常是重量级的对象,饿汉式可能造成创建了对象但是没有使用。 private static GirlFriend gf = new GirlFriend("xh"); /** * 如何保证我们只能创建一个GirlFriend对象 * 步骤[单例模式--饿汉式] * 一、将构造器私有化 * 二、在类的内部直接创建对象(该对象是静态的static) * 三、提供一个公共的static方法,可以返回对象 * * @param name */ private GirlFriend(String name) { this.name = name; } public static GirlFriend getInstance() { return gf; } @Override public String toString() { return gf.name; } }
懒汉式
package com.lyn.single_; import sun.security.jca.GetInstance; /** * 演示懒汉式单例模式 * * @author L.yn * @version 1.0 * @date 2021/12/25 20:42 */ public class SingleTon02 { public static void main(String[] args) { System.out.println(Cat.n1);//这一步不会创建对象 Cat cat = Cat.getInstance();//只在这步创建对象,并且只会创建一次 } } //希望在程序运行过程中,只能创建一个对象 //使用单例模式 class Cat { private String name; public static int n1 = 999; private static Cat cat; /** * 步骤 * 一、仍然构造器私有化 * 二、定义一个静态属性对象 * 三、提供一个公共public的static方法,可以返回一个Cat对象 * 四、懒汉式,只有当用户使用getInstance时,才会返回对象,然后再次调用时,会返回上次创建的Cat对象 * 从而保证了单例 * @param name */ private Cat(String name) { this.name = name; } public static Cat getInstance() { if (cat == null) { cat = new Cat("xb"); } return cat; } }
单例模式应用实例
饿汉式VS懒汉式
小结
当父类的某些方法,需要声明,但是又不确定如何实现时,可以将其声明为抽象方法,那么这个类就是抽象类
package com.lyn.abstract_; /** * @author L.yn * @version 1.0 * @date 2021/12/26 10:46 */ public class Abstract01 { public static void main(String[] args) { } } abstract class Animal { private String name; public Animal(String name) { this.name = name; } /** * 这里的eat其实没有什么意义 * 即父类方法不确定性的问题 * =====> 考虑将该方法设计为抽象(abstract)方法 * =====> 所谓抽象方法就是没有实现的方法 * =====> 所谓没有实现就是指,没有方法体 * =====> 当一个类中存在抽象方法时,需要将该类声明为abstract类 * =====> 一般来说,抽象类会被继承,由其子类来实现抽象方法 */ // public void eat() { // System.out.println("这是一个动物,但是不知道吃什么"); // } public abstract void eat(); }
最佳实践
需求
有多个类,完成不同的任务job
要求能够得到各自完成任务的时间
package com.lyn.abstract_; /** * @author L.yn * @version 1.0 * @date 2021/12/26 19:09 */ abstract public class Template { public abstract void job();//抽象方法 public void calculateTime() {//实现方法,调用 long start = System.currentTimeMillis(); job();//动态绑定机制 long end = System.currentTimeMillis(); System.out.println("任务执行的时间:" + (end - start)); } } package com.lyn.abstract_; /** * @author L.yn * @version 1.0 * @date 2021/12/26 18:51 */ 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; } } } package com.lyn.abstract_; /** * @author L.yn * @version 1.0 * @date 2021/12/26 18:55 */ public class BB extends Template { @Override public void job() { //得到开始时间 long num = 1; for (long i = 1; i <= 800000; i++) { num *= i; } } } package com.lyn.abstract_; /** * @author L.yn * @version 1.0 * @date 2021/12/26 18:54 */ public class TestTemplate { public static void main(String[] args) { AA aa = new AA(); aa.calculateTime();//任务执行的时间:1 BB bb = new BB(); bb.calculateTime();//任务执行的时间:2 } }
为什么有接口
接口快速入门
这样的设计需求在java编程/php//net/go中也是会大量存在的,一个程序就是一个世界,在现实世界存在的情况,在程序中也会出现,我们用程序来模拟一下
package com.lyn.inteface_; /** * 接口 * * @author L.yn * @version 1.0 * @date 2021/12/26 19:22 */ public interface UsbInterface { //规定接口的相关方法 void start(); void stop(); } package com.lyn.inteface_; /** * Phone类实现UsbInterface * 一、phone需要实现usbInterface接口 规定/声明的方法 * * @author L.yn * @version 1.0 * @date 2021/12/26 19:23 */ public class Phone implements UsbInterface { @Override public void start() { System.out.println("手机开始工作"); } @Override public void stop() { System.out.println("手机停止工作"); } } package com.lyn.inteface_; /** * @author L.yn * @version 1.0 * @date 2021/12/26 19:25 */ public class Camera implements UsbInterface { @Override public void start() { System.out.println("相机开始工作"); } @Override public void stop() { System.out.println("相机停止工作"); } } package com.lyn.inteface_; /** * @author L.yn * @version 1.0 * @date 2021/12/26 19:26 */ public class Computer { //编写一个方法,计算机开始工作 public void work(UsbInterface usbInterface) { //通过接口,调用方法 usbInterface.start(); usbInterface.stop(); } } package com.lyn.inteface_; /** * @author L.yn * @version 1.0 * @date 2021/12/26 19:22 */ public class Inteface01 { public static void main(String[] args) { //创建手机,相机对象 Phone phone = new Phone(); Camera camera = new Camera(); //创建计算机 Computer computer = new Computer(); /** * 手机开始工作 * 手机停止工作 * ===================== * 相机开始工作 * 相机停止工作 */ computer.work(phone);//把手机接入到计算机 System.out.println("====================="); computer.work(camera);//把相机接入到计算机 } }
基本介绍
接口就是给出一些没有实现的方法,封装到一起,到某个类要使用的时候,在根据具体情况把这些方法写出来
小结
在jdk7.0前,接口里的所有方法都没有方法体
jdk8.0后接口类可以有静态方法,默认方法,也就是说接口中可以有方法的具体实现
package com.lyn.inteface_; /** * @author L.yn * @version 1.0 * @date 2021/12/26 19:35 */ public interface AInterface { //写属性 int n1 = 10; //写方法 /** * 在接口中,抽象方法,可以省略abstract关键字 */ public void hi(); //在jdk8之后,可以有默认实现方法,需要使用default关键字修饰 default void ok() { System.out.println("ok..."); } //在jdk8之后,可以有静态方法 public static void cry(){ System.out.println("cry..."); } } package com.lyn.inteface_; /** * @author L.yn * @version 1.0 * @date 2021/12/26 19:36 */ public class Interface02 { public static void main(String[] args) { } } /** * 一、如果一个类 implements 实现接口 * 二、需要将该接口的所有抽象方法都实现 */ class A implements AInterface { @Override public void hi() { System.out.println("hi....."); } }
深入讨论
注意事项和细节
实现接口VS继承类
接口的多态特性
基本介绍
基本语法
package com.lyn.innerclass_; /** * @author L.yn * @version 1.0 * @date 2021/12/26 20:10 */ public class InnerClass01 {//外部其他类 public static void main(String[] args) { } } class Outer { private int n1 = 100;//属性 public void m1() {//方法 System.out.println("m1"); } { System.out.println("代码块"); } class Inner {//内部类,在Outer类的内部 } }
内部类的分类
定义在外部类局部位置上(比如方法内)
局部内部类(有类名)
说明:局部内部类是定义装载外部类的局部位置,比如方法中,并且有类名
可以直接访问外部类的所有成员,包含私有的
不能添加访问修饰符,因为它的地位就是一个局部变量,局部变量是不能使用修饰符的,但是可以使用final修饰,因为局部变量也可以使用final
作用域:仅仅在定义它的方法或代码块中
局部内部类—>访问---->外部类的成员【访问方式:直接访问】
外部类----->访问------->局部内部类的成员
【访问方式:创建对象,在访问】
【注意:必须在作用域内】
外部其他类----->不能访问--------->局部内部类【因为局部内部类地位是一个局部变量】
如果外部类和局部内部类的成员重名时,默认遵循就近原则,如果想访问外部类的成员,则可以使用(外部类名.this.成员)去访问
package com.lyn.innerclass_; /** * 演示局部内部类的使用 * * @author L.yn * @version 1.0 * @date 2021/12/26 20:17 */ public class LocalInnerClass { public static void main(String[] args) { Outer02 outer02 = new Outer02(); outer02.m1(); } } class Outer02 {//外部类 private int n1 = 200; private void m2() {//私有方法 System.out.println("m2"); } public void m1() {//方法 //一、局部内部类是定义在外部类的局部位置,通常实在方法 //三、不能添加访问修饰符,但是可以使用final修饰 //四、作用域:仅仅在定义它的方法或代码块中 String name = "XXX"; class Inner02 {//局部内部类(本质仍然是一个类) private int n1 = 800; //二、可以直接访问外部类的所有成员,包含私有的 public void f1() { //五、局部内部类可以直接访问外部类的成员 //七、如果外部类和局部内部类的成员重名时,默认遵循就近原则,如果想访问外部类的成员,则可以使用(外部类名.this.成员)去访问 /** * Outer02.this本质就是外部类的对象,即哪个对象调用了m1方法,Outer02.this就是哪个对象 */ System.out.println("外部类的n1: " + Outer02.this.n1 + "内部类的n1:" + n1); m2(); } } //六、外部类在方法中,可以创建Inner02实例,然后调用方法即可 Inner02 inner02 = new Inner02(); inner02.f1(); } }
记住
匿名内部类(没有类名,重点)
说明:匿名内部类是定义在外部类的局部位置,比如方法中,并且没有类名
本质:类
内部类
该类没有名字
同时还是一个对象
匿名内部类的基本语法
new 类或接口(参数列表){ 类体 };
package com.lyn.innerclass_; /** * 演示匿名内部类的使用 * * @author L.yn * @version 1.0 * @date 2021/12/26 20:40 */ public class AnonymousInnerClass { public static void main(String[] args) { } } class Outer04 {//外部类 private int n1 = 10; void method() { /** * 基于接口的匿名内部类 * 一、需求:想使用接口A接口并创建对象 * 二、传统方式,是写一个类实现该接口,并创建对象 * 三、可以使用匿名内部类来简化开发 * 四、a的编译类型是?A * 五、a的运行类型是?就是匿名内部类XXXX ===> Outer04$1 * 我们看底层会分配类名Outer04$1 * class XXXX implements A{ * @Override * public void cry() { * System.out.println("老虎叫唤..."); * } * } * * 六、jdk底层在创建匿名内部类 Outer04$1,立即马上就创建了Outer04$1实例,并且把地址返回给tiger * 七、匿名内部类使用一次,就不能在使用 */ A tiger = new A() { @Override public void cry() { System.out.println("老虎叫唤..."); } }; System.out.println("tigger的运行类型是:" + tiger.getClass()); //演示基于类的匿名内部类 /** * 分析: * 一、father的编译类型 Father * 二、father运行类型 Outer04$2 * 三、顶层会创建匿名内部类 * class Outer04$2 extends Father{} */ Father father = new Father("tom") { }; System.out.println("father的运行类型是:" + father.getClass());//Outer04$2 Father father1 = new Father("tom"); System.out.println("father1的运行类型是:" + father1.getClass());//Father } } interface A {//接口 void cry(); } class Father { public Father(String name) { } public void test() { } }
匿名内部类的语法比较特殊,请大家注意,因为匿名内部类即使一个类的定义,同时它本身也是一个对象,因此从语法上看,它既有定义类的特征,也有创建对象的特征,对前面代码分析可以看出这个特点,因此可以调用匿名内部类方法
可以直接访问类的所有成员,包含私有的
不能添加访问修饰符,因为它的地位就是一个局部变量
作用域:仅仅在定义他的方法或代码块中
匿名内部类---->访问 ——>外部类成员 【访问方式:直接访问】
外部其他类---->不能访问------>匿名内部类【因为匿名内部类地位是一个局部变量】
如果外部类和匿名内部类的成员重名时,匿名内部类访问的话,默认遵循就近原则,如果想访问外部类的成员,则可以使用(外部类.this.成员)去访问
package com.lyn.innerclass_; /** * @author L.yn * @version 1.0 * @date 2021/12/26 21:00 */ public class AnonymousInnerClassDetail { public static void main(String[] args) { new Outher05().f1(); } } class Outher05 { private int n1 = 99; public void f1() { //创建一个类基于匿名内部类 Person person = new Person() { @Override public void hi() { //可以直接访问外部类的所有成员,包含私有的 super.hi(); } }; person.hi();//动态绑定,运行类型是Outer05$1 //也可以直接调用,匿名内部类本身也是返回对象 new Person() { @Override public void hi() { System.out.println("hi"); } }.hi(); } } class Person { public void hi() { System.out.println("Person.Hi"); } }
定义在外部类的成员位置上
成员内部类(没有static修饰)
说明:成员内部类是定义在外部类的成员位置,并且没有static修饰
可以直接访问外部类的所有成员,包含私有的
可以添加任意访问修饰符,因为它的地位就是一个成员
作用域
成员内部类 ----- 访问 ------外部类(比如:属性)【访问方式:直接访问】
外部类------访问-----内部类【访问方式:创建对象,在访问】
外部其他类-----访问----成员内部类
package com.lyn.innerclass_; /** * @author L.yn * @version 1.0 * @date 2021/12/28 19:51 */ public class MemberInnerClass01 { public static void main(String[] args) { Outer08 outer08 = new Outer08(); outer08.t1(); //外部其他类,使用成员内部类的三种方式 Outer08.Inner08 inner08 = outer08.new Inner08(); //第二种方式,在外部类中,编写一个方法,放回Inner08 Outer08.Inner08 inner081 = outer08.getInner08(); //第三种方式 Outer08.Inner08 inner082 = new Outer08().new Inner08(); } } class Outer08 {//外部类 private int n1 = 10; public String name = "张三"; //注意:成员你内部类是定义在外部类的成员位置上 class Inner08 {//成员内部类 public void say() { //可以直接访问外部类的所有成员,包含私有的 System.out.println(n1 + " " + name); } } //写方法 public void t1() { //使用了成员内部类 Inner08 inner08 = new Inner08(); inner08.say(); } public Inner08 getInner08() { return new Inner08(); } }
静态内部类(使用static修饰)