static
类变量:也就是静态变量,是所有该类的对象共享的,static变量在类加载的时候就生成了,可通过对象名访问,也可通过类名直接访问,要遵守修饰符规则。
1. 类变量也叫静态变量/静态属性,是该类的所有对象共享的变量。任何一个该类的对象去访问它,取到的都是相同的值,同样,任何一个对象去修改它,修改的也是同一个变量。
2. 定义语法(常用): 访问修饰符 static 数据类型 变量名;
3. 访问类变量: 类名.类变量名[推荐以这种形式访问]
对象名.类变量名
4. 类变量在类加载时就初始化了,也就是说,即使没有创建对象,只要类加载了,类变量就可以使用了。
5. 只要类还存在,类变量就存在,与对象无关。
类方法:也叫静态方法。
public static 数据返回类型 方法名(){}
类方法的调用与类变量相似。推荐使用类名直接访问。
类名.类方法名
1. 类方法不允许使用和对象有关的关键字(this、super)
2. 类方法中只能访问静态变量或静态方法。(普通成员方法既可以访问静态的也可以访问非静态的)
main
另外,在main()方法中,可以访问main方法所在类的静态属性静态方法,但是不能访问该类的非静态方法,必须实例化一个该类的对象去访问。
代码块
基本语法:
[修饰符] {
代码;
};// (;)可省略
注意: 修饰符只能是static。带static的是静态代码块,不带的是普通代码块
使用细节:
1. static代码块作用是对类的初始化,随着类的加载而执行,并且只执行一次。普通代码块,每创建一个对象,就执行一次。(使用类的静态成员时,普通代码块不会被调用。)
2. 类在什么时候加载:【重点】
1)创建对象实例时(new)
2)创建子类对象实例,父类也会被加载
3)使用类的静态成员时(静态属性、静态方法)
3. 创建一个对象时,在一个类中调用顺序:
1) (在创建对象时,先加载了类)调用静态代码块和静态属性初始化,它俩优先级相同,取决于定义顺序
2) 调用普通代码块和普通属性初始化,它俩优先级相同,取决于定义顺序
3) 调用构造方法
4. 构造器的最前面其实隐藏了super()和 调用普通代码块,静态相关的代码块和属性,在类加载时就执行完毕,因此,是优先于构造器和普通代码块执行的
class AA{
public AA(){//构造器
//这有隐藏的执行要求
//(1) super()
//(2) 调用代码块
println("输出");
}
}
5. 创建一个子类时的调用顺序,看下图
final
final关键字用于表示常量。它可以与变量,方法和类一起使用。
任何实体(变量,方法或类)一旦被声明final后,只能分配一次。也就是,
-
final变量不能用另一个值重新初始化
-
final方法不能被重写
-
final类不能被继承
1. final修饰的属性又叫常量,用XX_XX_XX来命名。
2. final修饰的属性必须赋初值,且不能再修改,可再以下位置赋值:
1) 定义时,如 private final double TAX_RATE = 0.21;
2)在构造器中
3) 代码块中,
3. 如果final修饰的是static属性时,只能在定义时or静态代码块中赋值。
4. final类不能被继承,可以实例化对象
5. final方法不能被重写,但可以被继承。
6. final不能修饰构造方法
7. ★final往往搭配static使用,效率更高,它不会导致类加载,底层编译器做了优化处理。
public class Test {
public static void main(String[] args) {
System.out.println(AA.num);
}
}
class AA{
public final static int num = 100;
public static void method(){
System.out.println("调用了静态方法");
}
}
测试可知,只是输出了num,并没有输出方法里的语句,说明类并没有加载。
abstract
抽象类不能被实例化对象。 abstract只能修饰类和方法。
抽象类可以没有抽象方法。但有抽象方法的类必须是抽象类。
抽象类可以有任何成员,抽象类也是类。
抽象方法不能有主体(即不能加 { } )。
如果一个类继承了抽象类,那它比须实现抽象类的所有抽象方法,除非他自己也是抽象类。
抽象方法不能再用private\final\static修饰了,因为他们的作用是相反的。(前者需要继承,后者不需要继承)
接口
接口(interface): 接口就是将一些没有实现的方法封装到一起,到某个类要使用的时候,再根据具体情况将这些方法写出来
语法:
interface 接口名{
//属性
//方法
}
当别的类需要接入接口时,
class AA implements 接口名{
自己属性;
自己方法;
必须实现接口中的抽象方法;
}
注意: jdk7.0前接口中的方法没有方法体。jdk8.0后接口中的方法可以有静态方法、默认方法。
interface Usb{
void work(); //抽象方法
default public void test(){
//默认方法
}
public static void test2(){
//静态方法
}
}
细节:
1. 接口不能被实例化
2. 接口中所有方法都是public方法,接口中的抽象方法可以省略掉“abstract”
3. 普通接口要想接入接口,必须实现接口中的所有抽象方法。抽象类不需要。
4. 一个类可以实现多个接口: class AA implement 接口1, 接口2{ }
5. 接口中的属性都是final的,而且是 public static final 比如:
int a = 1; 实际上是 public static final int a = 1;
访问形式: 接口名.属性名
6. 接口不能继承其他类,但可以继承多个其他接口,多个。
interface AA extends BB, CC; // AA BB CC 都是接口名
7. 接口的修饰符只能是public和默认,和类一样。
接口vs继承
继承的话,子类自动拥有父类的技能。如果子类想扩展技能的话,可以通过接口来实现。
可以理解 接口 是对 继承 的一种补充。
接口的作用:设计,设计好各种规范(方法),让其他类去实现
继承的作用:解决代码的复用性和可维护性。
接口比继承更灵活。
接口同继承一样,有多态特性:
可以向上转。向下转。有多态数组
另外,接口存在多态传递现象:
内部类
众所周知,类有五大成员,分别是,属性、方法、构造器、代码块,还有本节的主角--内部类。
内部类又根据定义位置不同分为:
定义在外部类的局部位置上(方法、代码块中):
1. 局部内部类(有类名) 2. 匿名内部类(无类名)
定义在外部类的成员位置上:
1. 成员内部类 2. 静态内部类
其中,匿名内部类较常用,也就是很重要!!!
局部内部类
/**
* 局部内部类
*/
public class LocalInnerClass {
public static void main(String[] args) {
Outer01 outer01_xx = new Outer01();
outer01_xx.method();
}
}
class Outer01{
private int n1 = 20;
public void method(){
//1. 局部内部类定义在类的局部位置,通常是方法或代码块中
//2. 局部内部类不能用访问修饰符,除了final
//3. 局部内部类本质上还是一个类(可以有类该有的东西)
//5. 作用域:仅仅在定义他的方法或代码块
class innerClass{//有类名
//4. 局部内部类可以直接访问外部类的成员
private int n1 = 80;
public void innerMethod(){
// 如果外部类和内部类的成员重名,遵循就近访问原则。
// 如果想访问外部类成员,可以使用(外部类名.this.成员)去访问
//因为Outer01.this本质上就是外部类的对象,相当于outer01_xx(在main中),
// 可用hashcode值来验证
System.out.println("n1 = " + n1 + "\n外部类的成员n1 = " + Outer01.this.n1);
f1();
}
}
//外部类可以通过创建内部类对象来访问内部类
innerClass innerClass = new innerClass();
innerClass.innerMethod();
}
private void f1(){
System.out.println("外部方法");
}
private void f2(){
//new innerClass()
//外部其他类不能访问局部内部类,因为局部内部类是个局部变量;
}
}
匿名内部类
/**
* 匿名内部类
*/
public class AnonymousInnerClass {
public static void main(String[] args) {
Outer03 outer03 = new Outer03();
outer03.method();
}
}
class Outer03{//外部类
public void method(){//方法
//一、基于接口的匿名内部类
//假设现在需要使用接口,传统方法是写一个类,实现该接口,并创建对象
//Tiger tiger = new Tiger(); 或者:
IA tiger = new Tiger();
tiger.cry();
//但是需求是Tiger/Dog只使用一次,后面不再使用
//因此可以使用匿名内部类来简化开发
// cat 编译类型 IA,
// cat 运行类型 是匿名内部类 (底层分配类名 xxx ---> Outer03$1)
/*
底层:
class xxx implements IA{
public void cry() {
System.out.println("小猫喵喵叫~~~");
}
}
*/
//jdk底层在创建匿名内部类时,就立即创建了Outer03$1实例,并把地址返回给了cat
//匿名内部类使用一次,就不能在使用了。
// 因为(个人理解)只在创建匿名内部类时创建了实例,之后就不能再创建了
//但是这个对象cat是一直存在的,可以一直使用。
IA cat = new IA() {
public void cry() {
System.out.println("小猫喵喵叫~~~");
}
};
cat.cry();
//二、基于类的匿名内部类
//Father father = new Father("jack"); 这样不加括号就是正常的创建了一个对象
/*
底层:
class Outer03$2 extends Father{
@Override
public void test() {
System.out.println("重写了test方法");
}
}
// 同时也返回了 匿名内部类Outer03$2的对象 给了father
*/
Father father = new Father("jack"){//匿名内部类,加括号
//可以重写test的方法
@Override
public void test() {
System.out.println("重写了test方法");
}
};
// 三、 基于抽象类的匿名内部类(必须实现抽象方法)
new Animals(){
void cry(){
System.out.println("小猪 哼哼叫~~~");
}
};
}
}
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{
String name;
public Father(String name) {
this.name = name;
}
public void test(){
}
}
abstract class Animals{
abstract void cry();
}
注意匿名内部类底层的实现,以及它的运行类型是什么。
匿名内部类 可直接访问外部类成员,包括私有的。匿名内部类不能加访问修饰符。作用域仅在定义它的方法或代码块中。
外部类不能访问匿名内部类
匿名内部类有几种使用方法如下:
class Outer04{//外部类
public void Method(){
//匿名内部类的使用一
Person p = new Person("Tom"){
@Override
public void introduce() {
super.introduce();
}
};
p.introduce();
//匿名内部类的使用二
new Person("Jerry"){
public void hi(){
System.out.println("hi my name is Jerry");
}
}.hi();
}
}
class Person{
private String name;
public Person(String name) {
this.name = name;
}
public void introduce(){
System.out.println("我的名字是:" + name);
}
}