类变量
[修饰符] class 类{
[其他修饰符] static 数据类型 变量名;
}
- static 用来修饰的结构:属性、方法; 代码块、内部类;(构造器不能够用static修饰)
- 当类被加载进内存时,静态成员变量随着类一起进入方法区,有些jdk版本存放在堆中
- 静态成员变量依赖于类,不依赖于对象,只要类加载了,就可以访问。
类方法
[修饰符] static 数据返回类型 方法名(){}
- 静态方法可以被子类继承,但不能被子类重写
- 静态方法内只能使用静态成员。(属性和方法的前缀使用的是
当前类
,可以省略) - 普通方法都可以访问
- 类方法中没有this参数,不允许使用super和this
代码块
[修饰符] {
代码
}
修饰符可选,分为静态代码块和普通代码块
静态代码块只能调用静态成员
作用:如果多个构造器中都有重复的语句,可以放在代码块中
执行时机:
静态代码块,随着类的加载而执行,并且只会执行一次
普通代码块,创建对象时,每创建一个对象就执行一次
类加载的三种情况
- 创建对象实例时(new)
- 创建子类对象实例,父类也会被加载
- 使用类的静态成员时(静态属性,静态方法)
如果只是使用类的静态成员(并没有创建对象)时,普通代码块并不会执行
代码块执行过程
静态代码块和静态属性初始化,看顺序
普通代码块和普通属性初始化,看顺序
静态成员在类加载就执行完毕,然后才执行到构造器
构造器隐含super和普通代码块,super到父类,先找普通代码块和普通属性,再构造器,再子类
final
- 类不希望被继承
- 父类方法不希望被重写
- 不希望类的某个属性的值被修改
- 不希望某个局部变量被修改
public class test {
public static void main(String[] args) {
System.out.println(A.num);
}
}
class A {
//final修饰的属性又叫常量
//有了final和static就不会导致类加载,输出100
public final static int num = 100;
static {
System.out.println("A静态代码块被执行");
}
}
抽象类
描述:父类的方法不知道如何实现,需要子类来重写,可以把这个方法声明为抽象方法,这个类就是抽象类
abstract class Animal {
String name;
int age;
abstract public void cry();
}
特点
- 抽象类不一定有抽象方法,抽象方法必须是抽象类
- 抽象类本质还是类,可以有任意成员
- 抽象类不能实例化(但还包含构造器,子类实例化需要调用父类的构造器)
- 抽象类会被继承,由其子类来重写所有抽象方法,除非子类也声明为抽象类
- 抽象方法不能用private、final和static来修饰,因为这些关键字都是和重写相违背的。
接口
接口就是声明一组能力的类,接口不去实现,当实现了该接口,就拥有了该接口中声明的方法(即能力)
细节
- 接口不能被实例化
- 一个普通类实现接口,就必须将该接口的所有抽象方法都实现才能实例化,否则必须声明为抽象类,抽象类实现接口,可以不用实现接口的方法
- 一个类可以同时实现多个接口
- 接口可以继承多个别的接口
- 接口中属性的访问形式:接口名.属性名
接口多态
一个类的父类和这个类实现的所有接口都被称为超类,这个类称为子类
子类型对象的引用,可以被超类引用变量接收,这就是对象的多态
接口类型的变量可以指向实现了这个接口的对象实例,这是接口的多态
Interface A{
}
class B implements A {
}
Interface interface = new B();
多态传递
public class Demo {
public static void main(String[] args) {
B b = new Son();
A a = new Son();//OK!
}
}
interface A {}
interface B extends A {}
class Son implements B {}
内部类
分类
局部内部类
class OutClass {
int a = 10;
int b = 0;
public void method() {
class InnerClass {
int b = 1;
//内部类直接访问外部类成员,包含私有的
//成员重名就近原则,可以使用 外部类名.this.成员 访问
public void method() {
System.out.println(OutClass.this.b);
System.out.println(b);
}
}
//外部类在方法中,可以创建内部类对象,访问成员
InnerClass innerClass = new InnerClass();
innerClass.method();
}
}
特点:
- 可以访问外部类的所有成员,包含私有的
- 不能添加访问修饰符,但可以用final修饰,因为局部变量也可以用final
- 定义在方法或代码块中,作用域仅仅在定义它的方法或代码块中
- 本质上还是类
匿名内部类
new 父类(参数列表){
//匿名内部类的实现部分
};
new 父接口(){
//匿名内部类的实现部分
};
根据多态,可以用父类/接口,来接收该匿名内部类对象
父类名 匿名内部类对象名 = new 父类(参数列表){
//匿名内部类的实现部分
}
特点:
- 定义外部类的局部位置
- 匿名内部类没有名字,是一个具体的对象,且继承了一个类/实现了一个接口。
- 可以访问外部类的所有成员,包含私有的,重名就近原则,外部类名.this.成员
- 不能添加访问修饰符,它的地位就是一个局部变量
- 外部其他类不能访问匿名内部类
基于接口的匿名内部类
1、tiger的编译类型 IA
2、tiger的运行类型 匿名内部类 Outer$1
/*
运行类型(匿名内部类)的底层
底层实现IA接口里的方法
class Outer$1 implements IA {
@Override
public void cry() {
System.out.println("老虎叫");
}
}
*/
3、jdk底层在创建匿名内部类 Outer$1,立即马上就创建了Outer$1实例并且把地址返回给tiger
4、匿名内部类使用一次,就不能再使用,对象可以反复使用
public class Main {
public static void main(String[] args) {
IA t = new IA() {
@Override
public void say() {
System.out.println(123);
}
};
t.say();
}
}
interface IA {
void say();
}
基于类的匿名内部类
1、fahter编译类型 Fahter
2、father运行类型 Outer$2
3、底层会创建匿名内部类
/*
类和类是继承
class Outer$2 extends Father {
@Override
public void say() {
System.out.println("匿名内部类重写了say方法");
}
}
*/
4、同时也直接返回了 匿名内部类 Outer$2的对象
5、参数列表"jack"会传递给Father的构造器,没必要重写构造器
public class Main {
public static void main(String[] args) {
Father father = new Father("jack"){
@Override
public void say() { 继承了父类,如果没重写,会找父类的方法
System.out.println("重写了say方法");
}
};
}
}
class Father {
private String name;
public Father(String name) {
this.name = name;
}
public void say(){}
}
new Person(){
@Override
public void ok(String name) {
super.ok(name);
}
}.ok("jack");
基于抽象类的匿名内部类
必须重写抽象类中的所有方法
public class Main {
public static void main(String[] args) {
F father = new F(){
@Override
void a() {}
@Override
void b() {}
};
}
}
abstract class F {
abstract void a();
abstract void b();
}
应用
public class Test {
public static void main(String[] args) {
//当做实参传递,简洁高效,只用一次
f1(new IL() {
@Override
public void show() {
System.out.println("这是一副名画");
}
});
}
public static void f1(IL il) {
il.show();
}
}
interface IL {
void show();
}
成员内部类
public class Main {
public static void main(String[] args) {
Outer outer = new Outer();
outer.t1();
//外部其他类,使用成员内部类的两种方式
//1
Outer.Inner inner = outer.new Inner();
inner.say();
//2、在外部类中,编写一个方法,可以返回Inner对象
Outer.Inner innerInstance = outer.getInnerInstance();
innerInstance.say();
}
}
class Outer {
private int n1 = 10;
class Inner {
private double sal = 99.9;
public void say() {}
}
//返回一个成员内部类的实例
public Inner getInnerInstance() {
return new Inner();
}
public void t1() {
Inner inner = new Inner();
inner.say();
}
}
特点:
- 可以访问外部类的所有成员,包含私有的
- 重名就近原则,外部类名.this.同名成员名
- 可以添加任意访问修饰符
外部类名.内部类名 内部类对象 = 实例.new Inner()
public static void main(String[] args) {
//创建外部类实例
Outer outer = new Outer();
//外部类名.内部类名 内部类对象 = 实例.new Inner()
Outer.Inner inner = outer.new Inner();
}
静态内部类
特点:
- 可以访问外部类的所有成员,包含私有的,但不能直接访问非静态成员
- 重名就近原则,外部类名.this.同名成员名
- 可以添加任意访问修饰符
public class Test {
public static void main(String[] args) {
//1、创建Animal的静态的成员内部类的实例
Animal.Dog dog = new Animal.Dog();
dog.eat();
//2、创建Animal的非静态的成员内部类的实例
//Animal.Bird bird = new Animal.Bird(); 错误,要先创建对象
Animal a1 = new Animal();
Animal.Bird bird = a1.new Bird();
bird.eat();
}
}
class Animal {
//静态的成员内部类
static class Dog {
public void eat() {
System.out.println("狗吃骨头");
}
}
//非静态的成员内部类
class Bird {
public void eat() {
System.out.println("鸟吃虫子");
}
}
}
public class t {
public static void main(String[] args) {
Outer outer = new Outer();
outer.m1();
//外部其他类 使用静态内部类
//1、静态内部类,是可以通过类名直接访问(前提是满足访问权限,不能是private)
Outer.Inner inner = new Outer.Inner();
inner.say();
//2、编写一个方法,可以返回静态内部类的对象实例
Outer.Inner inner1 = outer.getInner();
inner1.say();
//3、编写一个静态方法,可以返回静态内部类的对象实例
Outer.Inner inner2 = Outer.getInner_();
inner2.say();
}
}
class Outer {
private int n1 = 10;
public static String name = "张三";
//4、
static class Inner {//成员内部类
private String name = "李四";
public void say() {
System.out.println(name);
System.out.println(Outer.name);
}
}
public void m1() {
Inner inner = new Inner();
inner.say();
}
public Inner getInner() {
return new Inner();
}
public static Inner getInner_() {
return new Inner();
}
}