文章目录
一、类和对象的定义
类:具有一系列相同的属性和行为的物体
public class TestDemo{//类命名:驼峰命名法
public static void main(String[] args) {//主函数
People people = new People("不吃香菜","女",2);
//new:1.在堆上开辟内存 jmap - histo:live进程号>1.log
// 2.()调用无参构造函数(用来初始化当前对象属性)
String name = people.getName;
System.out.println(name);
people.play();
}
}
常见访问限定符:public 默认 protected private
public class People{
//实例变量(所有函数共用一份成员变量) 默认this 指向当前对象
private String name,sex;
private int age;
//Alt+Insert -> 初始化
//构造函数/方法 -> 初始化成员变量,给类型默认值
//如果当前类中没有自定义构造函数,JVM默认生成无参构造函数 类型默认值
//如果自己实现了构造函数,JVM不会再生成无参构造
public People(String name, String sex, int age) {//发生命名冲突时,必须显示this
this.name = name;//变量 就近原则
this.sex = sex;
this.age = age;
}
//行为:成员方法/实例方法(函数)
public void play(){
System.out.println(this.name+"打王者荣耀");
}
public String getName(){
return name;
}
}
二、对象大小判断
对象大小:对象头(16) + 实例变量 + 内存填充(以16为单位进行内存对齐)
对象头:虚拟机64位:16,虚拟机32位:8
具体判断方法:
- 首先建立文件TestDemo.java和一个空类People.java
- 在当前目录下打开命令窗口,通过javac编译测试文件,java运行文件
注意: 一个类对应一个字节码文件(如下)
- 在该目录下重新打开一个命令窗口,通过jps查看当前正在运行的java程序,找到TestDemo对应的进程号,运用命令(jmap-histo:live 进程号)查看堆上活着的TestDemo程序,将查找到的信息通过输出符号(>)输出到指定文件1.log
- 打开文件1.log,通过查找 People 可以看到定义的空类在堆上占用了16个字节
- 在People中定义变量
int a;
static int b;
int c;
其中,a,c称为实例变量,b称为静态变量(static关键字中详细介绍)
定义之后重新运行程序,进行上述过程查看该类所占字节数
三、函数重载
特点:
- 同一个类
- 函数名相同,参数列表不同(类型、个数)
public void eat(){
System.out.println("吃饭");
eat("zs");//调用下边的函数
}
public void eat(String name){
System.out.println("指定"+name+"吃饭 ");
}
public People(String name, String sex, int age) {
this.name = name;
this.sex = sex;
this.age = age;
}
public People(){//无参函数调用有参函数
this("zs","男",1);//this调用构造函数
}
重载和重写的区别?
- 方法的重载和重写都是实现多态的方式,区别在于 重载 实现的是 编译时 的多态性,而 重写 实现的是 运行时 的多态性。
- 重载:一个类中,函数名相同、参数列表不同(参数类型不同、参数个数不同或者二者都不同)。
- 重写:子类对父类 的方法进行重写。参数相同,返回值类型可以不相同,但是必须是父类返回值的派生类(即外壳不变,核心重写)。重写的好处在于子类可以根据需要,定义特定于自己的行为。
四、常用关键字
this
- 如果 局部变量 和 成员变量 产生命名冲突,需要显示指明当前对象 this.name = name;
- 构造函数重载:
1)构造函数之间不能用 this() 相互调用
2)this() 调用构造函数时,不能在成员函数当中调用
3)当前构造函数不能通过 this() 调用多个构造函数
注意:this() 调用 必须位于当前构造函数有效代码(不包含注释)第一行
super
- 用于在子类构造函数中调用父类的构造
- 只能在构造函数之间调用,不能在成员方法中使用super()
注意:必须位于当前构造函数有效代码第一行
final
修饰:
变量 final int INITSIZE = 10;
// 常量
方法 final void fun(){ }
//不允许重写
类 final class String( )
//不能被继承
static
static可以修饰变量、方法、类。
成员变量 = 实例变量 + 静态变量
静态变量又称为类变量,用static关键字修饰
class A{
int a;//实例变量
static int b;//静态变量
}
(一)静态变量 和 实例变量 区别?
- 存储位置不同
实例变量 -> Java 堆 内存中
静态变量 -> 方法区 - 是否和对象有关
实例 变量:与对象有关。随着对象创建二存在,随着对象被回收而释放。实例化一次,对应一个实例变量的生成(实例变量所属于对象)。
静态变量:跟对象无关,只与类有关。随着类的加载而存在,随着类的消失而消失。一个类唯一只有一份静态变量(静态变量所属于类)。 - 调用
实例变量:只能被 对象 调用。
静态变量 :可以被 对象 和 类 调用。
(二)静态方法 和 实例方法的区别?
- 实例方法
属于 对象 ,必须先实例化,通过 (对象.方法名) 调用,可以访问 实例成员 和 静态成员。实例方法中可以使用 this、super关键字(this -> 指向当前对象、super -> 指向父类。 - 静态方法
属于 类,可以直接通过 (类.方法名) 调用,只能访问 静态成员。静态方法不能被重写,private方法不会被重写。
内部类
Java 一个类中可以嵌套另外一个类,称为内部类。
- 如果内部类与外部类没有同名方法或属性,内部类可以直接调用外部类的方法和字段
- 如果内部类有同名方法或属性,必须使用"外部类名.this.方法名/属性"格式调用
class SingleLink{
private Node head;
//内部类
class Node{
private int value;
public void fun(){
head = null;//若需要直接访问外部类,则需要设计为实例内部类
//实例内部类 head前面隐含了 SingleLink。this.
}
static public void fun(){
//若不需要访问外部类,则设计为静态内部类
//静态内部类 不包含 SingleLink。this.
}
}
}
非静态内部类
- 非静态内部类必须先实例化外部类,然后创建内部类的对象来实现。
使用示例:
静态内部类
静态内部类使用 static 关键字定义,静态内部类不需要创建外部类来访问,可以直接访问:
- 注意:静态内部类无法访问外部类的成员。
- 实例内部类相比与静态内部类有额外的开销,因为通过Out拿到了外部类引用,所以它包含外部类的this。
类的初始化顺序
静态变量 静态块 实例变量 实例块 构造函数
代码测试:
class InstanceTest{
public InstanceTest(){
System.out.println("实例变量");
}
}
class StaticTest{
public StaticTest(){
System.out.println("静态变量");
}
}
class A{
private InstanceTest a = new InstanceTest();
private static StaticTest b = new StaticTest();
static {
//静态块 -> 专门用来初始化 静态变量
System.out.println("静态块");
}
{
//实例块 -> 专门用来初始化 实例变量
System.out.println("实例块");
}
public A(){
System.out.println("构造函数");
}
}
public class TestDemo {
public static void main(String[] args) {
A a = new A();
}
}
测试结果:
单例模式
-
特点:只能有一个实例、 必须自己创建自己的唯一实例、必须向其他对象提供这一实例,单例模式的构造函数必须私有,提供一个共有静态函数,返回实例对象。
-
懒汉单例模式
-
饿汉单例模式
-
使用场景:
需要频繁的进行创建和销毁的对象、创建对象时耗时过多或耗费资源过多,但又经常用到的对象、工具类对象、频繁访问数据库或文件的对象(比如数据源、session工厂等)。
类加载过程
类加载时机
- 创建类的实例(new)
- 访问某个类或接口的静态变量,或者对该静态变量赋值(static)
- 反射 (如Class.forName(“com.shengsiyuan.Test”))
- main函数所在的类优先被加载
- 先初始化父类,再初始化子类
子类的初始化顺序:
父类静态变量 -> 父类静态块 -> 子类静态变量 -> 子类静态块 -> 父类实例变量 -> 父类实例块 -> 构造 -> 子类实例变量 -> 子类实例块 -> 构造
类加载过程
- 第一大阶段:装载阶段
加载产物:Class对象(保存当前类的所有信息)
类加载器、双亲委派模型:
使用双亲委派模型的优点:避免类的重复加载、安全性高 - 第二大阶段:链接阶段
1)验证:验证当前字节码格式,版本号等信息
2)准备:给静态变量开辟内存,并赋类型默认值
3)解析:符号引用解析为直接引用 - 第三大阶段:初始化阶段
给静态变量赋值操作、使用静态代码块为类变量指定初始值