文章目录
类变量和类方法
类变量
1.类变量(静态变量static修饰)是类所有对象共享的,jdk8以前是存放在方法区,jdk8及以后是存放在堆区
2.类变量是在类加载时就初始化了(即使没有创建对象,只要类加载,就可以使用类变量)。
3.类变量是该类的所有对象共享的,实例变量是每个对象独享的
基本语法:访问修饰符 static 数据类型 变量名
访问类变量(类方法一样):1.类名.类变量名(推荐使用)
2.对象名.类变量名(静态变量的访问修饰符的访问权限和范围和普通属性是一样的)
public class ChildGame {
public static void main(String[] args) {
Child child1 = new Child("白骨精");
child1.join();
child1.count++;
Child child2 = new Child("狐狸精");
child2.join();
child2.count++;
Child child3 = new Child("老鼠精");
child3.join();
child3.count++;
//===========
//类变量,可以通过类名来访问
System.out.println("共有" + Child.count + " 小孩加入了游戏...");
//下面的代码输出什么?
System.out.println("child1.count=" + child1.count);//3
System.out.println("child2.count=" + child2.count);//3
System.out.println("child3.count=" + child3.count);//3
}
}
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 + " 加入了游戏..");
}
}
public class VisitStatic {
public static void main(String[] args) {
//类名.类变量名
//说明:类变量是随着类的加载而创建,所以即使没有创建对象实例也可以访问
System.out.println(A.name);
A a = new A();
//通过对象名.类变量名
System.out.println("a.name=" + a.name);
}
}
class A {
//类变量
//类变量的访问,必须遵守 相关的访问权限.
public static String name = "韩顺平教育";
//普通属性/普通成员变量/非静态属性/非静态成员变量/实例变量(不同的叫法)
private int num = 10;
}
类方法
类方法也叫静态方法
基本语法:访问修饰符 static 返回类型 方法名(参数列表){}
类方法不能使用和对象有关的关键字(this和super),普通方法可以使用
类方法只能访问静态变量和静态方法,普通成员方法可以普通变量(方法)和静态变量(方法)
理解main方法语法static
public static void main(String【】args){}
1.main方法是java虚拟机调用,所以访问权限是public
2.java虚拟机在执行main()方法时不必创建对象,所以该方法必须是static
3.该方法接收String类型的数组参数,该数组中保存执行java命令时传递给所运行的类的参数
代码块
代码块又称为初始化块,属于类中的成员,类似于方法,通过{}包围起来,只有方法体(修饰符只有static或无(静态代码块,普通代码块)),加载类或者创建对象时隐式的调用,(对构造器的补充,做初始化操作)
代码块的使用细节
1.static 代码块也叫作静态代码块:
对类进行初始化,随着类加载而执行,并且只会执行一次。如果是普通代码块,每创建一个对象,就执行一次。 \textcolor{blue}{对类进行初始化,随着类加载而执行,并且只会执行一次。如果是普通代码块,每创建一个对象,就执行一次。} 对类进行初始化,随着类加载而执行,并且只会执行一次。如果是普通代码块,每创建一个对象,就执行一次。
2.什么时候类加载?
//1. 创建对象实例时(new)
AA aa = new AA();
//2. 创建子类对象实例,父类也会被加载, 而且,父类先被加载,子类后被加载
AA aa2 = new AA();
//3. 使用类的静态成员时(静态属性,静态方法)
System.out.println(Cat.n1);
class AA extends BB {}
3.创建一个对象时,在一个类的调用顺序:
1.调用静态代码块和静态属性初始化(定义顺序)
2.调用普通代码块和普通属性的初始化(定义顺序)
3.构造方法
public class CodeBlockDetail03 {
public static void main(String[] args) {
A a = new A();// (1) A 静态代码块01 (2) getN1被调用...(3)A 普通代码块01(4)getN2被调用...(5)A() 构造器被调用
}
}
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() 构造器被调用");
}
}
4.构造器的最前面其实隐含了super()和调用普通代码块(以及普通属性初始化)。静态相关的代码块,属性初始化,在类加载时,就执行完毕,因此是优先于构造器和普通代码块
class A{
public A(){
//1.super() 2.调用普通代码块
System.out.println("构造器被调用")
}
}
public class CodeBlockDetail04 {
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.子类的构造方法
单例设计模式
某个类只能存在一个对象实例,并且该类只能提供一个取的其对象实例的方法
步骤:1.构造器私有化(防止用户直接new)
2.类内部创建对象
3.向外暴露一个静态的公共方法
饿汉式
public class SingleTon01 {
public static void main(String[] args) {
// GirlFriend xh = new GirlFriend("小红");
// GirlFriend xb = new GirlFriend("小白");
//通过方法可以获取对象
GirlFriend instance = GirlFriend.getInstance();
System.out.println(instance);
GirlFriend instance2 = GirlFriend.getInstance();
System.out.println(instance2);
System.out.println(instance == instance2);//T
//System.out.println(GirlFriend.n1);
}
}
//有一个类, GirlFriend
//只能有一个女朋友
class GirlFriend {
private String name;
//public static int n1 = 100;
//为了能够在静态方法中,返回 gf对象,需要将其修饰为static
//對象,通常是重量級的對象, 餓漢式可能造成創建了對象,但是沒有使用.
private static GirlFriend gf = new GirlFriend("小红红");
//如何保障我们只能创建一个 GirlFriend 对象
//步骤[单例模式-饿汉式]
//1. 将构造器私有化
//2. 在类的内部直接创建对象(该对象是static)
//3. 提供一个公共的static方法,返回 gf对象
private GirlFriend(String name) {
System.out.println("構造器被調用.");
this.name = name;
}
public static GirlFriend getInstance() {
return gf;
}
}
懒汉式
public class SingleTon02 {
public static void main(String[] args) {
Cat instance = Cat.getInstance();
System.out.println(instance);
Cat instance2 = Cat.getInstance();
System.out.println(instance2);
System.out.println(instance == instance2);//T
}
}
class Cat {
private String name;
public static int n1 = 999;
private static Cat cat ; //默認是null
//4.懶漢式,只有當用戶使用getInstance時,才返回cat對象, 後面再次調用時,會返回上次創建的cat對象
private Cat(String name) {
System.out.println("構造器調用...");
this.name = name;
}
public static Cat getInstance() {
if(cat == null) {//如果還沒有創建cat對象
cat = new Cat("小可愛");
}
return cat;
}
}
final关键字
final可以修饰 类,方法,属性,局部变量
1.修饰类:该类无法被继承
2.修饰方法:无法被子类重写
3.修饰属性和局部变量:该值无法被修改
使用细节:
1.final修饰的属性又叫做常量,一般命名XX-XX
2.final修饰的属性必须赋初值,并且以后无法被修改,(1.定义时 2.构造器中 3.代码块中)
3.如果final修饰的属性是静态的,则初始化的位置只能在定义时和代码块中
4.final类不能被继承,但是可以实例化对象
5.final方法不能被重写,但是可以继承(前提该方法不是在final类下)
6.如果一个类是final类,就没必要将方法设置成final方法
7.final不能修饰构造器
8.final和static搭配使用,效率更高,修饰基本数据类型和String在调用时不会导致类加载
9.包装类(Integer,Double等都是final类)String类也是final类
抽象类
当父类的某些方法,需要声明,但是又不确定如何实现,可以将其声明为抽象方法,那这个类就是抽象类
public class Abstract01 {
public static void main(String[] args) {
//Animal an=new Animal() 错误 抽象类不能被实例化
//一个类继承了抽象类,则必须实现抽象类的所有抽象方法,除非自己声明为abstract
}
}
abstract class Animal { //抽象类不一定包含abstract方法,
//抽象类可以有任意成员,非抽象方法,构造器,静态属性
//抽象类的构造器不能被直接调用,因为抽象类本身不能被实例化。只有通过子类来创建对象时,才会调用抽象类的构造器。
//一旦包含了abstract方法,该类必须声明为abstract
//abstract只能修饰类和方法,不能修饰属性和其他
private String name;
//思考:这里eat 这里你实现了,其实没有什么意义
//即: 父类方法不确定性的问题
//===> 考虑将该方法设计为抽象(abstract)方法
//===> 所谓抽象方法就是没有实现的方法
//===> 所谓没有实现就是指,没有方法体
//===> 当一个类中存在抽象方法时,需要将该类声明为abstract类
//===> 一般来说,抽象类会被继承,有其子类来实现抽象方法.
// public void eat() {
// System.out.println("这是一个动物,但是不知道吃什么..");
// }
public abstract void eat() ;//抽象方法不能使用private,final和static修饰,因为和重写相违背
}
static与重写违背
被static修饰的父类方法,子类也可以取相同的方法名、相同的修饰符、相同的返回值类型,但实际上已经不是同一个方法了
class Father{
public static void father(){
System.out.println("father");
}
}
class Son extends Father{
public static void father(){
System.out.println("son");
}
}
class Test{
public static void main(String[] args){
Father son=new Son();
son.father();//输出的结果为father
}
}
接口
接口就是给出一些没有实现的方法,封装到一起。
基本语法:interface 接口名{ class 类名 implements 接口{
//方法 自己的方法;
} 必须实现的接口方法;}
在jdk8以前,接口的所有方法都没有方法体,即都是抽象方法,jdk8及以后有静态方法和默认方法,可以有方法的具体实现
public interface AInterface {
//6.接口中的属性,只能是final的,而且是 public static final 修饰符
public int n1 = 10; //等价 public static final int n1 = 10;
//在接口中,抽象方法,可以省略abstract关键字
public void hi();
//在jdk8后,可以有默认实现方法,需要使用default关键字修饰
public default void ok() { //显式的default关键字和默认(default)访问修饰符不是一回事
System.out.println("ok ...");
}
//在jdk8后, 可以有静态方法
public static void cry() {
System.out.println("cry ....");
}
}
public class InterfaceDetail01 {
public static void main(String[] args) {
//new IA();错误//1.接口不能被实例化
}
}
//2.接口的修饰符和类一样,public和默认
interface IA {
void say(){};//3.接口中所有的方法是 public方法, 接口中抽象方法,可以不用abstract 修饰
}
interface IB {//interface IB extends IA{ 4.接口不能继承其他类,但是可以继承其他接口
void hi(){};
}
class Cat implements IA,IB{
//5.一个类同时可以实现多个接口
//6.一个普通类实现接口,就必须将该接口的所有方法都实现,可以使用alt+enter来解决
@Override
public void say() {
}
@Override
public void hi() {
}
}
abstract class Tiger implements IA {}//7.抽象类去实现接口时,可以不实现接口的抽象方法
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() {
}
}
区别抽象类:
接口 | 抽象类 | |
---|---|---|
属性修饰符 | public static final (可以省略) | 任意 |
构造方法 | 无 | 有 |
普通方法 | 需要default或者static关键字修饰 (访问修饰符public可不写) | 任意 |
抽象方法 | public abstract(可以省略) | 不能省略abstract |
内部类
一个类的内部又完整的嵌套了另一个类结构,被嵌套的称为内部类,嵌套其他类的类称为外部类。内部类可以直接访问私有属性,并且可以体现类的包含关系。
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类的内部
}
}
局部内部类
class Outer02 {//外部类
private int n1 = 100;
private void m2() {
System.out.println("Outer02 m2()");
}//私有方法
public void m1() {//方法
//1.局部内部类是定义在外部类的局部位置,通常在方法
//2.可以直接访问外部类的所有成员,包含私有的
//3.不能添加访问修饰符,但是可以使用final 修饰
//4.作用域 : 仅仅在定义它的方法或代码块中
final class Inner02 {//局部内部类(本质仍然是一个类)
private int n1 = 800;
public void f1() {
//5. 局部内部类可以直接访问外部类的成员,比如下面 外部类n1 和 m2()
//7. 如果外部类和局部内部类的成员重名时,默认遵循就近原则,如果想访问外部类的成员,
// 使用 外部类名.this.成员)去访问
// ssOuter02.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();
}
}
匿名内部类
语法: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 Father {//类
public Father(String name) {//构造器
System.out.println("接收到name=" + name);
}
public void test() {//方法
}
}
abstract class Animal { //抽象类
abstract void eat();
}
匿名内部类使用细节
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...");
}
}
成员内部类
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);
}
}
静态内部类
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();
}
}