目录
类与对象
类是一组具有相同特征的对象的抽象,类中定义了所有该类的对象所共同具备的属性和方法
对象是某个类的一个具体的实体,使用new关键字创建一个对象
类的定义
- 在一个源文件中,只可以存在一个主类(
public class
) - 类的命名使用有意义的大驼峰(第一个单词首字母大写,若类名为多个单词,每个单词首字母大写)单词命名法
- 成员变量未初始化,其值为对应数据类型的默认值。引用数据类型的默认值为null
- 定义类的时候,可以给成员变量初始化
- null在Java中表示“空引用”,使用null调用对象时会报NULL Pointer Exception
实例化对象
//Person是一个类
Person per = new Person();
//new person() 是在堆中开辟对象的过程,要使用该对象,需要借助它的引用,即per(per在栈中开辟),per保存的是new出来的对象的地址
类的方法既不存储在栈中,也不存储在堆中,有个专门的方法区存储该类的方法,该类的所有对象共同使用该类的方法。
static关键字
看见static,其修饰的东西和对象无关
static不能直接修饰一个外部类->编译出错
static可以修饰:
属性,称为类属性、类变量,所有对象共享
为什么要有static修饰的变量?
- 普通的成员变量必须通过该类的对象来访问,当一个成员变量被static关键字修饰,它就表示类的属性,该类的所有对象共享这个属性,所有对象的该属性值都一样。
static关键字修饰的属性在
JVM
的方法区中存储static修饰的属性,通过类名可以直接访问,当然也可以用类实例化的所有对象来访问(不推荐)
类中的所有方法都在方法区中存储,包括一些常量、静态变量
在Java中,能否在一个方法的内部定义静态变量?
不能!!! 在方法中定义的变量都属于局部变量(局部变量在使用前必须赋值,不存在默认值),存储在栈中,函数栈帧结束后,其内部的局部变量都被释放。静态变量是存储在方法区中
static 修饰的变量和 final修饰的变量有何区别?
final 修饰成员常量,在类中定义时必须初始化。为了节省不必要的空间浪费,在类中定义常量,一般都会使用全局常量 static final ,常量的命名规则是:所有字母全部大写,各个单词之间用下划线分隔。
class Person{ static String country = "中国"; } Person per = null; per.country是可以输出的 //per虽然没有保存指向一个实实在在对象的地址,但它已经被定义为Person类型的引用,在此处相当于Person.country,直接用类名来调用静态变量
方法,称为类方法,工具方法
为什么主方法是个静态方法?
主方法是程序入口,若主方法是成员方法,则调用它必须先产生类的对象,通过对象调用。但没进入程序就无从谈起产生对象
静态方法能否访问成员域(成员变量和成员方法)?
不能!! 静态方法不产生对象就可以调用,而成员域需要产生对象才能调用;
静态方法中只能调用静态方法或静态属性,static家族之间可以相互调用
成员方法能否访问静态属性和静态方法?
可以
共享的变量设计为静态变量
工具类的方法设计为静态方法,如Arrays提供的方法
代码块,称为静态代码块
内部类,称为静态内部类
面向对象三大特性
封装
封装是为了实现保护性和易用性
用private进行封装只是封装的一种
权限修饰符
按访问范围从小到大依次划分为:private < default(也称为包访问权限,不用写出来,缺省权限修饰符的时候就是default) < protected < public
public
被public修饰的内容,在当前项目中都是可见的,都是可以使用的
private
只在当前类的内部可见,出了其所在的类,外部是不可见被private修饰的内容的
private关键字不能修饰外部类
getter()和setter()接口
如果想在类的外部使用类内部的私有属性,使用类提供的getter()和setter()方法(getter()是读取,setter()是修改)
编码规范:Java类中所有成员变量一律使用private封装,并根据属性的实际情况对外提供setter()和getter()接口
所有引用数据类型判断其是否相等,使用 .equals()方法。如
String str1 = "qwer";
String str2 = "wer";
if(str1.equals(str2)){
System.out.println("相等");
}else{
System.out.println("不相等");
}
//若要判断两个引用的对象是否相等,使用str1.equals(str2),返回值为boolean.其他引用类型比较是否相等都用.equals()方法。
补充
编译错误和运行时错误
编译错误是指程序运行不起来,语法有错误;
运行时错误是指语法校验通过,程序可以跑起来,倒是跑到某一行出错了(逻辑错误),比如数组越界异常、空指针异常等等。
public class Pvf{
static boolean Paddy;
public static void main(String args[]){
System.out.println(Paddy); //编译和运行都能通过,输出为false。
//要执行主方法,必须加载类Pvf,加载Pvf的过程中就给类变量Paddy初始化了(初始值是boolean类型的默认值false)。
}
}
继承
多态
构造方法
构造方法的作用就是产生对象,使用new关键字产生一个对象时,分为两个步骤:1、为对象在堆中开辟一块空间,空间大小由该类中成员变量的属性决定;2、调用对象的构造方法,为所有成员变量赋初始值。
语法规则
- 构造方法名称与类名称完全相同
- 构造方法没有返回值声明(不是void)
- 一个类中至少有一个构造方法,若没有显式定义,编译器会生成一个默认的无参构造方法。当类中手动定义了构造方法,默认的无参构造方法不再生成。
构造方法的重载
重载只可能是由于参数个数的不同
调用有参构造函数时,成员变量的赋值过程
无论有参构造函数还是无参构造函数,调用时分三步
- 将所有成员变量初始化为所属类型的默认值(这里是构造函数假设所有成员变量都没有初始值)
- 为了验证上述假设是否正确,先不执行构造函数代码块内部的内容,先回到定义成员变量的地方。对于有初始值的成员变量,则成员变量的值变为定义时设置的初始值;若该成员变量没有设置初始值,则此时的成员变量的值就是第一步中设置的默认值
- 这里再执行代码块里面的内容(无参构造函数的赋值过程只有1-2步),将有参构造函数传进来的参数值赋给对应的成员变量
成员变量的赋初始值过程实际上就发生在构造方法中
注意事项
- **一个对象不能调用自己的构造方法! **
- 构造方法是在产生对象时由
JVM
调用
this关键字
this表示当前对象的引用
this能否出现在静态方法中?
不能!!!静态方法既可以通过类名调用,也可以通过对象调用,静态方法内部必须同时满足这两种情况。通过类名调用时,不存在对象,自然也就不能存在this了。
this关键字可以
调用当前对象的成员变量
在构造方法的形参和成员变量重名时,使用this.成员变量表示成员变量
调用当前对象的方法(调用普通成员方法;构造方法的相互调用)
调用成员方法:
在类中的一个成员方法中调用另一个成员方法,使用this.方法名(),不加this也可以运行,编译后会自动加上this
构造方法的相互调用:
若不同参数的构造方法之间出现了重复的内容,使用this(参数)调用其他构造方法。
- this调用其他构造方法必须放在此构造方法的首行
- this调用构造方法不能成"环"!你调我,我调你,进入死循环。
表示当前对象的引用
当前的属性或方法是哪个对象调用的,this就指代该对象
public class Test02 { static class Student{ private String name; public void whoAmI(){ System.out.println(this); } } public static void main(String[] args) { Student stu = new Student(); System.out.println(stu); stu.whoAmI(); } } //输出 Test02$Student@1540e19d Test02$Student@1540e19d //1540e19d是stu的值,stu保存new出来的Student类的一个对象的模糊地址,即1540e19d是该对象的模糊地址(真实地址对程序员不可见)
代码块
使用{}括起来的代码称为代码块
根据定义的代码块的位置以及关键字的不同分为四种
普通代码块
定义在方法中,用{}括起来的代码块。
成员代码块(构造块)
直接定义在类中,不加任何修饰符的代码块。优先于构造方法执行,有几个对象产生就调用几次构造块。
同步代码块(多线程)
静态代码块
定义在类中,使用static修饰的代码块。在类加载时执行一次,与产生多少对象无关。类只要一加载,静态块就首先被执行,且只执行一次。
public class Test03 {
public static void main(String[] args) {
System.out.println(Animal.age); //输出结果为 300,age被赋值了四次,1处age = 0;2处age = 10; 3处age = 100;4处age = 300; 按照1->2->3->4的顺序进行赋值
}
}
class Animal{ //1
static int age = 10; //2
static {
age = 100; //3
}
static {
age = 300; //4
}
}
编译后各个静态块合为一体:
static int age;
static {
age = 10;
age = 100;
age = 300;
}
Animal类在定义的时候,类变量age被保存到方法区中,age的初始值赋值过程发生在类加载时。类加载时,若类变量有初始值,先将其初始值赋给类变量,再执行静态块。
Java中final和static修饰的变量是在什么时候赋值的?
这部分引用自博文Java中final和static修饰的变量是在什么时候赋值的?
ConstantValue
属性是源文件编译后的class文件中,字段表的属性。ConstantValue
属性的作用是通知虚拟机自动为静态变量赋值,只有被static关键字修饰的类变量才可以使用ConstantValue
这项属性来进行初始化,否则使用这项属性也会被JVM
忽略。
final修饰的实例变量
用final修饰的实例变量是在实例构造器方法里面赋值的,也就是对象创建的时候赋值。
static修饰的类变量
类加载
类加载的过程可分为:
- 加载:将字节码所代表的静态存储结构转化为方法区的运行时数据结构。
- 验证:验证字节码格式,确保Class文件的字节流中包含的信息符合当前虚拟机的要求,并且不会危害虚拟机自身的安全。
- 准备:创建类或者接口的静态字段,并为静态变量设置初始值。
- 解析:将常量池内的符号引用替换为直接引用。
- 初始化:执行类构造器方法。方法就是把所有静态变量的赋值动作以及所有静态代码块收集到一起,合并产生一个方法。
static修饰的类变量可以用两种方法赋值:1、通过ConstantValue属性赋值;2、在类构造器方法中赋值。
目前Oracle公司实现的Javac编译器的选择是:
- final+static修饰:使用ConstantValue属性赋值。
- 仅使用static修饰:在方法中赋值。这个方法在类加载的初始化阶段执行。
使用ConstantValue属性赋值
用生成ConstantValue属性来进行初始化,这个变量必须是基本类型或者java.lang.String类型。因为Class文件格式的常量类型中只有与基本属性和字符串相对应的字面量,所以就算ConstantValue属性想支持别的类型也无能为力。
总结
- 单独用final修饰的变量也有可能在字节码找到对应的ConstantValue属性,但是会被JVM忽略掉。
- final修饰的实例属性,在实例创建的时候才会赋值。
- static修饰的类属性,在类加载的准备阶段赋初值,初始化阶段赋值。
- static+final修饰的String类型或者基本类型常量,JVM规范建议在初始化阶段赋值,但是HotSpot VM直接在准备阶段就赋值了。
- static+final修饰的其他引用类型常量,赋值步骤和第二点的流程是一样的。
还有一点,一定不要把《深入理解Java虚拟机》和《Java虚拟机规范》搞混了。
- 《Java虚拟机规范》是官方JVM规范文档翻译而来的,所有的JVM实现都要遵从规范,但有强制要求的规范和建议的规范。
- 《深入理解Java虚拟机》是作者根据自己的理解,结合HotSpot VM的具体实现,为了让读者更容易理解JVM而写的一本书。
匿名对象
new出来的对象,没有引用指向,使用一次后就被销毁。匿名对象常用于进行测试类中的某些功能。
toString
方法(注意大小写)
当一个引用类型的变量调用println()
打印,得到的是该变量保存的指向的对象的模糊地址,实际上我们需要得到的是该对象属性值,为此需要在类中实现对应的toString方法
static class Student{
private String name;
private int age = 10;
private String sex = "男";
public String toString(){
String ret = "name= "+name+", age= "+age+", sex= "+sex;
return ret;
}
public Student(String sex){
this.sex = sex;
}
}
public static void main(String[] args) {
System.out.println(new Student()); //输出为:name= null, age= 10, sex= 男
System.out.println(new Student("女")); //输出为:name= null, age= 10, sex= 女
}