类变量和类方法
类变量
有时需要很多个对象对同一个变量进行更改,那么我们就需要一个类变量(静态变量)
-
使用static修饰的属性
-
每一个该类实例化的对象都可以共享该变量
-
可以使用类名+.的方法调用该变量
类变量存放在方法区的静态域里(逻辑上属于堆空间)
类方法
与普通成员方法的区别就是用static修饰
-
类方法不需要创建对象就可以使用
-
要求类方法不涉及任何和对象相关的成员
类方法和普通方法都随着类的加载而加载,将结构信息存储在方法区,类方法中无this参数
理解main方法的语法
public static void main(String[] args){}
-
JVM需要调用类的main方法,所以该对象的访问权限必须是public
-
JVM在执行main方法时不必创建对象,所以该方法必须是static
-
该方法接收String类型的数组参数,该数组保存执行java命令时传递给所运行的类的参数
-
java执行的程序所需要的参数依次存入args[]
代码块及静态调用的顺序
代码块化也称初始化块,属于类中的方法,类似于方法,将逻辑语句封装在方法体中,通过{}包围起来
-
static{代码};(要么写static,要么什么都不写直接就是{代码};)
-
普通代码块/静态代码块
-
;可写可不写
-
static代码块(静态代码块),随着类的加载而执行且只执行一次;普通代码块,每创建一个对象就执行一次
-
类什么时候被加载
-
创建对象实例时
-
创建子类实例,父类也会被加载
-
使用类的静态成员变量时
-
-
普通代码块在实例化时隐式调用(创建一次就调用一次),若只使用类的静态成员时,普通代码块并不会执行
public class Main { public static void main(String[] args) { Test.a++; Test.a++; Test test = new Test(); Test test0 = new Test(); Test test1 = new Test(); Test test2= new Test(); } } class Test{ static int a = 0; static { System.out.println("静态代码块"); } public Test() { } { System.out.println("普通代码块"); } }
-
创建对象时,在一个类中调用的顺序为:
-
调用静态代码块和静态属性初始化(按顺序来,优先级相同)
-
调用普通代码块和普通属性的初始化(同上)
-
调用构造方法
-
package com.wang.Advanced; public class Main { public static void main(String[] args) { Test.a++; Test.a++; Test test = new Test(); } } class Test{ static int a = 0; static { System.out.println("静态代码块"); } { System.out.println("普通代码块1"); } { System.out.println("普通代码块5"); } public Test() { System.out.println("我是构造方法"); } }
-
构造方法的最前面隐含了super()和调用普通代码块
静态成员的一切在类加载时就执行完毕,因此优先于构造器和普通代码块
-
静态代码块只能直接调用静态成员,普通代码块可以调用任意成员
-
当有继承关系时,调用顺序如下
-
父类的 静态
-
子类的 静态
-
父类的 普通
-
父类的 构造
-
子类的 普通
-
子类的 构造
-
单例设计模式
设计模式:目的是实现代码的高内聚和低耦合(23种)
-
解决软件开发某些特定问题而提出的一些解决方案
-
遵循六大原则
-
开闭原则
-
里氏代换原则
-
单一职责原则
-
接口隔离原则
-
依赖倒转原则
-
最少知道原则(迪米特法则)
-
单例设计模式:采取一定的方法保证整个软件系统中,对某个类只能存在一个对象实例,并且该类只提供一个取得其对象实例的方法
-
饿汉式
-
懒汉式
单例设计模式的步骤(饿汉式):主程序中还不需要该对象时,类中就已经通过静态类方法创建好等着了
-
构造器私有化(防止用户直接new创建对象)
-
类的内部创建对象(需要将其修饰为static)
-
向外暴露一个静态的公共方法(getInstance)
-
代码实现
package com.wang.Advanced; public class SingletonModeTest { public static void main(String[] args) { Mother mother = Mother.getInstance(); Mother mother1 = Mother.getInstance(); System.out.println(mother.getName()); System.out.println(mother1.getName()); } } //有一个类,Mother //只能有一个mama class Mother{ private String name; private Mother(String name){ this.name = name; } private static Mother mother = new Mother("maa"); public static Mother getInstance(){ return mother; } public String getName() { return name; } }
单例设计模式的步骤(懒汉式):使用的时候才创建(饿汉式可能造成内存浪费)
public class SingletonModeTest { public static void main(String[] args) { Mother mother = Mother.getInstance(); Mother mother1 = Mother.getInstance(); System.out.println(mother.getName()); System.out.println(mother1.getName()); } } //有一个类,Mother //只能有一个mama class Mother{ private String name; private Mother(String name){ this.name = name; } public static Mother mother; //在没有调用getInstance方法之前,只是声明了mother对象,并没有创建 //如果调用时发现还未创建就创建,已创建就直接返回 public static Mother getInstance(){ if(mother==null) mother = new Mother("mama"); return mother; } public String getName() { return name; } }
final
用于修饰类,属性,方法和局部变量
-
不希望类被继承
-
不希望父类的某些方法被子类重写
-
不希望类的某个属性(变量)被修改
-
final修饰的属性也叫常量用下划线区分单词
-
final修饰的属性可以在以下情况赋值(但不能修改)
-
定义时
-
构造器中
-
代码块中
-
-
如果final的属性是static,以上三种方法中构造器就不能使用了
-
final类不能被继承但是可以实例化
-
如果类不是final类,但是含有final的方法,该方法不能继承,但是可以重写
-
final和static往往搭配使用,效率更高,底层编译器做了优化处理
-
包装类(Intger...)和String类都是final类
抽象类
当父类的某些方法需要声明但不知道如何实现时,可以将其声明为抽象方法,这个类就是抽象类
-
当一个类中存在抽象方法时,该类也必须声明为abstract
-
抽象类的价值就在于设计,由子类实现
abstract class Animal{ private String name; public Animal(String name){ this.name=name; } //抽象方法没有方法体 public abstract void eat(); }
-
抽象类不能被实例化
-
抽象类可以没有抽象方法
-
abstract只能修饰类和方法,不能修饰属性和其他的
-
如果一个类继承了抽象类,则他必须实现抽象类的所有抽象方法,除非他也为自己声明为抽象类
接口
接口给出一些没有实现的方法,封装到一起
-
接口不能实例化
-
接口中所有方法都是public方法
-
接口中的抽象方法,可以不用abstract修饰
-
一个类要实现接口,就必须将该接口的所有方法都实现
-
抽象类实现接口,可以不用实现接口方法
-
一个类同时可以实现多个接口
-
接口中的属性只能是public final static修饰符
-
一个接口只能继承别的接口
-
接口的修饰符只能是public和默认
接口的多态
-
多态参数,若某方法形参为接口,传递的实参可以是实现了接口功能的各种对象
-
多态数组,类似继承的多态
-
多态传递
public class Test { public static void main(String[] args) { IG ig = new Teacher(); //多态传递 IH ih = new Teacher(); } } interface IH{ } //多态传递 interface IG extends IH{} class Teacher implements IG{}
继承VS接口
-
继承在于代码复用和维护
-
接口在于设计
-
接口比继承更灵活
-
接口一定程度上实现了代码解耦
内部类
一个类的内部又完整的嵌套了另一个类结构,被嵌套的类称为内部类
-
内部类可以直接访问私有属性,并且可以体现类与类之间的包含关系
-
定义在外部类局部位置上的(比如方法内)
-
局部内部类(有类名)
-
匿名内部类(没有类名)
-
-
定义在外部类成员位置上
-
成员内部类(没用static修饰)
-
静态内部类(使用static修饰)
-
局部内部类
是定义在外部类的局部位置的
-
可以直接访问外部类的所有成员,包含私有的
-
不能添加访问修饰符,但是可以用final修饰
-
作用域:仅仅在定义他的方法和代码块中
-
局部内部类访问外部类的成员是直接访问
-
外部类的成员访问局部内部类需要先实例化内部类的对象,再去访问(必须在作用域内)
-
外部其它类不能访问局部内部类,局部内部类实际上是一个局部变量
-
如果外部类和局部内部类成员重名时,默认遵循就近原则,也可以外部类名.this.成员去访问
package com.wang.Advanced; import java.security.DigestException; //外部其它类 public class TestInnerclass { } //外部类 class Outer{ private int a = 100; public Outer(int a) { this.a = a; } public void m1(){ System.out.println("我是方法"); //局部内部类Inner_,在m1方法下 //除了final修饰符,别的修饰符都不能用 //Inner_类只能在方法m1中使用 final class Inner_{ public void f1(){ //局部内部类可以直接访问外部类的属性包括私有成员 System.out.println(a+"我是局部内部类"); } public Inner_() { } } //外部类在方法m1中可以创建局部内部类Dog的对象,然后调用方法 Inner_ inner_ = new Inner_(); inner_.f1(); } { System.out.println("我是代码块"); } }
匿名内部类 (重要!!!!)
-
本质是类
-
是内部类
-
该类没有名字
-
同时还是一个对象
基于接口的匿名内部类:
package com.wang.Advanced; import java.security.DigestException; //外部其它类 public class TestInnerclass { public static void main(String[] args) { new Outer(50).m1(); } } //外部类 class Outer{ private int a = 100; public Outer(int a) { this.a = a; } public void m1() { //基于接口的匿名内部类 //1.需要使用接口A,并创建对象 //传统方法:写一个类,实现该接口,并创建对象 //2.匿名内部类的方法 //a的编译类型是接口 //a的运行类型是匿名内部类 /*/ 底层实现的匿名内部类XXXX: class XXXX implements A{ @Override public void AA() { System.out.println("AAAAA"); } } XXXX => Outer$1 */ //3.jdk底层匿名内部类创建完成后,立即创建了XXXX的实例,并且返回地址给a //4.匿名内部类使用一次就不能再使用了,但是实例对象已经创建好了,可以反复调用 A a = new A(){ @Override public void AA() { System.out.println("AAAAA"); } }; System.out.println(a.getClass()); //此时匿名内部类已经消失了,但是还维持着对象和调用它的地址 a.AA(); a.AA(); a.AA(); } } interface A{ public void AA(); }
基于类的匿名内部类:
如果是抽象类,那么必须重写抽象方法
//基于类的匿名内部类 //编译类型是B //运行类型是Outer$2 /* class Outer$2 extends B{ } 返回了Outer$2的对象 */ B b = new B(){ }; } class B{ }
使用场景
当做实参直接传递
package com.wang.Advanced; import java.security.DigestException; //外部其它类 public class TestInnerclass { public static void main(String[] args) { //此处传递的实参就是匿名内部类的实现对象 f1(new A() { @Override public void AA() { System.out.println("这是接口实现的方法"); } }); } //静态方法,形参是接口类型 public static void f1(A a) { a.AA(); } } interface A{ public void AA(); }
成员内部类
成员内部类定义在外部类的成员位置,并且没有static修饰
-
作用域:和其他成员一样
-
和属性一样,可以访问外部类,外部类和外部其它类都可以直接访问,不需要实例化对象
public class TestInnerclass { public static void main(String[] args) { Outer outer = new Outer(); outer.m1(); } } class Outer{ private int a= 10; //成员内部类,与局部内部类不同的是,成员内部类不用写在方法下 class Inner01{ public void say(){ //可以直接访问所有成员,包括私有的 System.out.println(a+"a"); } } public void m1(){ //使用成员内部类 Inner01 inner01 = new Inner01(); inner01.say(); } }
静态内部类
在成员内部类前加static
-
只能访问外部类的静态成员(直接访问)
-
可以添加任意修饰符
-
作用域(同成员内部类)
-
外部类访问必须创建对象再访问(与普通成员内部类不同)
-
外部其它类可以直接访问静态内部类
package com.wang.Advanced; import java.security.DigestException; //外部其它类 public class TestInnerclass { public static void main(String[] args) { Outer outer = new Outer(); outer.m1(); //外部其它类访问静态内部类,外部类名.静态内部类名 Outer.Inner01 inner01 = new Outer.Inner01(); inner01.s(); } } class Outer{ private int a= 10; private static final int b = 10; //静态内部类,放在外部类的成员位置,被static修饰 //可以直接访问外部类的所有静态成员,但不能访问非静态成员,访问a就会出错 public static class Inner01{ public void s(){ System.out.println(b); } } //外部类访问必须创建对象再访问(与普通成员内部类不同) public void m1(){ Inner01 inner01 = new Inner01(); inner01.s(); } }
异常
正常情况下,抛出异常后程序就崩溃退出了,之后的代码就不再执行
所以需要异常处理机制
如果程序猿认为一段代码可能出现问题,可以使用try-catch异常处理机制处理
-
选中该代码块->ctrl + alt + t->try-catch
-
捕获异常后,会继续进行程序
异常分为
-
Error :JVM无法解决的严重问题
-
Exception:其他因编程错误或偶然的外在因素导致的一般问题
-
运行时异常
-
编译时异常
-
异常体系图
常见的运行时的异常
-
NullPointerException空指针异常
-
ArithmeticException数学运算异常
-
ArrayIndexOutOfBoundsException数组下标越界异常
-
ClassCastException类型转换异常
-
NumberFormatException数字格式不正确异常
编译异常
一般发生在网络,数据库,文件操作编程中
异常处理的机制
-
try-catch-finally 程序员在代码中捕获发生的异常,自行处理
-
throws 将发生的异常抛出,交给调用者(方法)来处理,最顶级的处理者就是JVM
-
通常将释放资源的代码放在finally上
-
JVM处理异常的机制就是直接输出错误信息然后退出
try-catch
try中的语句一旦发生异常,接下来的程序就不在运行,而是直接进入catch块
枚举
-
枚举是一组常量的集合
-
枚举属于一种特殊的类,里面只包含一组有限的特定的对象
枚举的实现方式:
-
自定义类实现枚举
-
枚举对象通常为只读(不需要set方法)
-
对枚举对象/属性使用final+static共同修饰,实现底层优化
-
枚举对象通常使用全部大写
-
枚举对象根据需要也可以有多个属性
-
package com.wang.Advanced.Enum; public class TestEnum { public static void main(String[] args) { System.out.println(Season.AUTUMN.getDesc()); } } class Season{ private String name; private String desc; //直接在类内创建固定的对象 public final static Season SPRING = new Season("春天","c"); public final static Season WINTER = new Season("冬天","d"); public final static Season AUTUMN = new Season("秋天","q"); public final static Season SUMMER = new Season("夏天","x"); //构造方法设置为private,防止直接new //去掉set方法,防止属性被修改 private Season(String name, String desc) { this.name = name; this.desc = desc; } public String getName() { return name; } public String getDesc() { return desc; } }
-
enum关键字实现枚举
package com.wang.Advanced.Enum; public class TestEnum0 { public static void main(String[] args) { System.out.println(Season2.SPRING.getDesc()); } } //使用enum关键字来实现枚举类 enum Season2{ //1.使用关键字enum替代class //2.public final static Season2 SPRING = new Season2("春天","c"); //变为SPRING("春天","c"); 常量名(实参列表) //3.如果有多个常量,使用逗号间隔即可 //4.使用枚举时,必须将定义常量写在最前面 SPRING("春天","c"),WINTER("冬天","d"); private String name; private String desc; private Season2(String name, String desc) { this.name = name; this.desc = desc; } public String getName() { return name; } public String getDesc() { return desc; } }
-
使用enum关键字开发枚举类时,默认会继承Enum类,而且是final类
-
多个枚举对象用逗号间隔
Enum类的各种方法的使用
注解
基本注解
-
@Override重写
-
@Deprecated表示某个类或方法已过时
-
@SupressWarnings抑制编译器警告
元注解
-
@Target是修饰注解的注解,称为元注解