7.静态、抽象、内部类
第七章【静态、抽象、内部类】
一、修饰符
1、static
static修饰符可以修饰属性(成员变量)、(成员)方法、代码块
①静态属性
静态属性是属于类的,可以直接使用类名来访问,也可以使用对象访问,但推荐使用类名访问
- 可以用来修饰成员变量,使用static修饰的成员变量一般叫做静态变量,不使用static修饰的成员变量叫做实例变量
- 使用static修饰变量,则这个变量在类加载到内存的时候就会给静态变量在方法去申请内存空间,并由jvm分配初始值
- 使用方法:类名.静态变量
- 静态变量的存在与对象的存在没有关系,静态变量属于类,被这个类创建的所以的对象共享
②静态方法
-
静态方法中不能调用类中的非静态方法或非静态属性
-
静态方法中,不能访问this,也不能使用super
-
类加载的时候jvm会给静态方法分配内存空间,
-
使用方法:方法名();
③静态代码块
静态代码块,也叫做静态初始化代码块,它的作用就是给类中的静态属性做初始化的
static{} 静态代码块
- 执行类加载的时候由jvm自上而下自动调用当前类中的静态代码块
- 只会自动被执行一次,因为JVM在一次运行中,对一个类只会加载一次
{} 非静态(匿名)代码块
-
每构建一次对象,非静态代码块就会从上往下被调用一次
-
子类构造器调用之前执行
-
非静态属性的显示赋值,是在 父类构造器执行结束之后 和 子类中的匿名代码块执行之前
的时候
④静态导入
在自己的类中,要使用另一个类中的静态属性和静态方法,那么可以进行静态导入,导入完成
后,可以直接使用这个类中的静态属性和静态方法,而不用在前面加上类名
2、final
①修饰类
用final修饰的类不能被继承,也就是说这个类是没有子类的
②修饰方法
用final修饰的方法可以被子类继承,但是不能被子类的重写
③修饰变量
用final修饰的变量就变成了常量,并且它只能被赋一次值,第二次赋值就会报错
-
使用final修饰实例变量:
声明的同时进行初始化
匿名代码块中初始化
构造器中初始化
-
使用final修饰局部变量:
-
使用final修饰静态成员变量:
声明的同时赋值
静态代码块中赋值
-
使用final修饰引用类型变量:
3、abstract
abstract修饰方法,那么该方法就是抽象方法;abstract修饰类,那么该类就是抽象类。
①修饰方法
抽象方法的特点:只有方法的声明;没有方法的实现,必须位于抽象类中,将来被抽象类的子类继承并重写
public abstract void test();
②修饰类
抽象类中可以编写抽象方法;
抽象类不能进行实例化创建对象,但内部可以构建构造器
抽象类中可以没有抽象方法,
有抽象方法的类,一定要声明为抽象类.
public abstract class Action{ }
二、interface
(引用数据类型:类、数组、接口)
1、概念
定义接口使用关键字 interface
接口中的属性都是公共的静态常量(常量的名字一般需要全大写,可以省略public static final)
接口中的方法都是抽象方法,不需要使用 abstract 修饰符,因为接口中的方法默认就是抽象方法
(JDK8及以后,还允许在接口中编写静态方法、默认方法、私有方法)
2、接口中没有构造器,不能直接构建对象,接口的目的是让他的实现类做实现,使用implements关键字
class xxx implements 接口1,接口2{}
3、接口继承
接口和接口之间是多继承
interface xxx extends 接口1,接口2{}
4、多态
final static 可以一起修饰方法
三、访问控制

正常编写的类(外部类),可以使用俩种权限控制修饰符:public和default
四、内部类
内部类一共分为四种形式:
- 成员内部类
- 静态内部类
- 局部内部类
- 匿名内部类(***)
1、内部类的声明方式
2、内部类能直接访问外部类的声明内容
3、外部类如何直接访问内部类的内容
4、内部类对象的构建
①静态内部类
1、内部类的声明方式
class Out{
//声明一个静态内部类(静态方法)
[修饰符] static class Inner{
//定义静态属性,静态方法,非静态属性,非静态方法,匿名代码块
}
}
2、内部类能直接访问外部类的声明内容
静态内部类可以直接访问外部类的静态属性和静态方法
3、外部类如何直接访问内部类的内容
外部类直接使用类名访问内部类中的静态内容
4、内部类对象的构建
静态内部类对象的构建不依托于外部类对象直接构建
当前类中: 内部类 变量名=new 内部类();
通用方式: 外部类.内部类 变量名 = new 外部类.内部类();
②成员内部类(实例内部类)
1、内部类的声明方式
public class Out{
public class Inner{
//只允许声明非静态的内容
}
}
2、内部类能直接访问外部类的声明内容
成员内部类可以直接访问所在的外部类的所有的静态或非静态的所有内容
如果内部类没有需要访问的内容则直接访问外部类中的内容;
如果内部类属性/方法和外部类同名,this.属性/方法访问的是内部类的;
访问外部类的内容:外部类.this.属性/方法
3、外部类如何直接访问内部类的内容
没有能直接访问的内容
4、内部类对象的构建
成员内部类对象的构建需要依托外部类对象的构建
(1)先构建外部类对象,再构建内部类对象
外部类 变量1 = new 外部类();
外部类.内部类 变量2 = 变量1.new 内部类();
(2)内部类和外部类一起构建
外部类.内部类 inner = new 外部类().new 内部类();
③局部内部类
只能在所在的方法中使用,不能使用范围修饰符static
1、内部类的声明方式
public class Out{
public void test(){
[修饰符] class Inner{
//只能声明非静态的内容
}
}
}
2、内部类能直接访问外部类的声明内容
能访问他所在方法中的final修饰的变量
访问自己类中没有的变量,默认访问所在方法的;方法中没有,默认访问外部类的
如果变量在自己类中有,默认访问自己的;如果自己没有,只能访问外部类的:外部类.this.变量(不能访问方法的)
3、外部类如何直接访问内部类的内容
什么都访问不到
4、内部类对象的构建
在所在的方法内部构建对象
④匿名内部类
1、匿名内部类的俩种形式:
利用一个父类,进行声明并创建匿名内部类对象,这个匿名内部类默认就是这个父类的子类型
利用一个接口,进行声明并创建匿名内部类对象,这个匿名内部类默认就是这个接口的实现类
2、匿名内部类因为没有类名:
匿名内部类必须依托于一个父类型或者一个接口
匿名内部类在声明的同时,就必须创建出对象,否则后面就没法创建了
匿名内部类中无法定义构造器
3、如果利用父类型声明这个匿名内部类,那么这个匿名内部类默认就是这个父类型的子类
如果利用接口声明这个匿名内部类,那么这个匿名内部类默认就是这个接口的实现类
假设现在已经确定了要使用内部类,那么一般情况下,该如何选择?
- 考虑这个内部类,如果需要反复的进行多次使用(必须有名字)
在这个内部类中,如果需要定义静态的属性和方法,选择使用静态内部类
在这个内部类中,如果需要访问外部类的非静态属性和方法,选择使用成员内部类
- 考虑这个内部类,如果只需要使用一次(可以没有名字)
选择使用匿名内部类
- 局部内部类,几乎不会使用
五、包装类
1、概述
针对这八种基本类型,JavaAPI又专门提供了对应的类类型,目的就是为了分别把这八种基本类型的数
据,包装成对应的类类型,这时候就变成对象了,就可以调用方法了或者访问属性了
2、JDK1.5或以上,可以支持基本类型和包装类型之间的自动装箱、自动拆箱
六、Object常用的方法
1、toString方法
该方法可以返回一个对象默认的字符串形式
2、getClass方法
它可以返回一个引用在运行时所指向的对象
子类中不能重写getClass,调用的一定是Object中的getClass方法
3、equals方法
该方法可以比较俩个对象是否相等
对equals方法重写,一般需要注意以下几点:
-
自反性:对任意引用obj,obj.equals(obj)的返回值一定为true.
-
对称性:对于任何引用o1、o2,当且仅当o1.equals(o2)返回值为true时,o2.equals(o1)的返回值一
定为true;
-
传递性:如果o1.equals(o2)为true, o2.equals(o3)为true,则o1.equals(o3)也一定为true
-
一致性:如果参与比较的对象没任何改变,则对象比较的结果也不应该有任何改变
-
非空性:任何非空的引用obj,obj.equals(null)的返回值一定为false
4、hashCode方法
该方法返回一个int值,该int值是JVM根据对象在内存的中的特征(地址值),通过hash算法计算出的一个结果
对于俩个对象的hashCode值:
-
相等的俩个对象,hashCode值一定相等
-
hashCode值相同,俩个对象有可能相等,也可能不同等
-
hashCode值不同,俩个对象一定不同
七、String对象
- 如果字符串使用字面量的形式声明,或者字符串拼接过程中使用的是字面量拼接的都是常量池中的
- new出来的字符串对象,默认都是在堆区中保存;字符串拼接过程中牵扯到变量,在堆区中保存。
- 强制去字符串常量池中获取intern()
- String是固定长度的字符串,书写成功无法修改,耗费内存(StringBuffer)
String str1 = "hello";
String str2 = "hello";
System.out.println(str1==str2); //true
String str3 = "a"+"b";
String str4 = "a"+"b";
System.out.println(str3==str4); //true
System.out.println("----------------------");
String str5 = new String("hello");
String str6 = new String("hello");
System.out.println(str5==str6); //false
System.out.println(str5==str1); //false
System.out.println(str5.intern()==str6.intern()); //true
String str7 = str1+str2;
String str8 = str1+str2;
System.out.println(str7==str8); //false
String str9 = str1+str2;
String str10 = "hello"+"hello";
System.out.println(str9==str10); //false
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· 终于写完轮子一部分:tcp代理 了,记录一下
· 震惊!C++程序真的从main开始吗?99%的程序员都答错了
· 别再用vector<bool>了!Google高级工程师:这可能是STL最大的设计失误
· 单元测试从入门到精通
· 【硬核科普】Trae如何「偷看」你的代码?零基础破解AI编程运行原理