类:我们叫做class。
对象:我们叫做Object,instance(实例)。某个类的对象=某个类的实例。
注意
- 所有类都继承于Object类,包括包装类、自己创建的类、String
- 基本数据类型与Object无关
总结
1.对象是具体的事物;类是对对象的抽象;
2.类可以看成一类对象的模板,对象可以看成该类的一个具体实例。
3.类是用于描述同一类型的对象的一个抽象概念,类中定义了这一类对象所应具有的共同的属性、方法。
类初始化
初始化过程
public class Person {String name;int age;public void get() {System.out.println("姓名:" + name + ",年龄:" + age);}}public class TestDemo {public static void main(String args[]) {// 第一种Person per = new Person() ;// 声明并实例化对象per.name = "张三" ;//操作属性内容per.age = 30 ;//操作属性内容per.get() ;//调用类中的get()方法//第二种Person per = null;//声明对象per = new Person() ;//实例化对象per.name = "张三" ;//操作属性内容per.age = 30 ;//操作属性内容per.get() ;//调用类中的get()方法}}
具体步骤:
- 加载Person.class文件进内存
- 在栈内存(保存的是堆内存的地址,在这里为了分析方便,可以简单理解为栈内存保存的是对象的名字)为s开辟空间
- 在堆内存(保存对象的属性内容。堆内存需要用new关键字来分配空间)为学生对象开辟空间
- 对学生成员变量进行默认初始化
- 对学生成员变量进行显示初始化
- 通过构造方法对学生对象的成员赋值
- 学生成员对象初始化完毕,将对象地址赋值给s变量。

任何情况下,只要看见关键字new,都表示要分配新的堆内存空间,一旦堆内存空间分配了,里面就会有类中定义的属性,并且属性内容都是其对应数据类型的默认值。
于是,上面两种对象实例化对象方式内存表示如下:
两种方式的区别在于①②,第一种声明并实例化的方式实际就是①②组合在一起,而第二种先声明然后实例化是把①和②分步骤来。
初始化顺序
类的代码在初次使用时才会被加载。 这通常指加载发生于创建类的第一个对象之时,但是当访问static域或者static方法时,也会被加载
- 静态代码块:在类加载的时候就执行,且执行一次。一般用于对对象进行初始化。
- 构造代码块:每次调用构造方法都执行且在构造方法之前执行。一般是对类进行初始化。
不存在继承的情况下,初始化顺序
- 静态变量,静态代码块 (按定义的书写顺序来进行初始化)
- 普通变量,普通代码块(按定义的书写顺序来进行初始化)
构造函数
public class InitializationSequence {// 静态属性private static String staticField = getStaticField();// 静态方法块static {System.out.println(staticField);System.out.println("静态方法块初始化");}// 普通属性private String field = getField();// 普通方法块{System.out.println(field);System.out.println("普通方法块初始化");}// 构造函数public InitializationSequence() {System.out.println("构造函数初始化");}public static String getStaticField() {String statiFiled = "Static Field Initial";System.out.println("静态属性初始化");return statiFiled;}public static String getField() {String filed = "Field Initial";System.out.println("普通属性初始化");return filed;}public static void main(String[] args) {InitializationSequence i =new InitializationSequence();}}/* 输出结果:静态属性初始化Static Field Initial静态方法块初始化普通属性初始化Field Initial普通方法块初始化构造函数初始化*/
存在继承的情况下,初始化顺序
- 父类(静态变量、静态语句块)(按定义的书写顺序来进行初始化)
- 子类(静态变量、静态语句块)(按定义的书写顺序来进行初始化)
- 父类(实例变量、普通语句块)(按定义的书写顺序来进行初始化)
- 父类(构造函数)
- 子类(实例变量、普通语句块)(按定义的书写顺序来进行初始化)
- 子类(构造函数)
class A{private static int a = printInit("static a");private int b = 9;int c =printInit("c");static {System.out.print(" X ");}private static int d = printInit("static d");{System.out.print(" F1 ");}int c1 =printInit("c1");A(){System.out.println("b:"+b+" c: "+c);c=39;}static int printInit(String s){System.out.println(s+" initialized");return 47;}}public class TestInit extends A {static int e =printInit("static e");int f=printInit("f");static {System.out.print(" Y ");}static int g =printInit("static g");{System.out.print(" F2 ");}TestInit(){System.out.println("f:"+f+"c:"+c);}public static void main(String[] args){System.out.println("QQQ");TestInit t = new TestInit();}}static a initializedX static d initializedstatic e initializedY static g initializedQQQc initializedF1 c1 initializedb:9 c: 47f initializedF2 f:47c:39
值传递和引用传递
Java 内的传递都是值传递,Java基本类型的传递是值传递,Java 中实例对象的传递是引用传递,实际是对象地址的值传递。
值传递
Java 语言的方法调用只支持参数的值传递。
当一个对象实例作为一个参数被传递到方法中时,参数的值就是对该对象的引用。对象的属性可以在被调用过程中被改变,但对对象引用的改变是不会影响到调用者的。
首先回顾一下在程序设计语言中有关将参数传递给方法(或函数)的一些专业术语。按值调用(call by value)表示方法接收的是调用者提供的值,而按引用调用(call by reference)表示方法接收的是调用者提供的变量地址。一个方法可以修改传递引用所对应的变量值,而不能修改传递值调用所对应的变量值。 它用来描述各种程序设计语言(不只是Java)中方法参数传递方式。
Java程序设计语言总是采用按值调用。也就是说,方法得到的是所有参数值的一个拷贝,也就是说,方法不能修改传递给它的任何参数变量的内容。
例一
public static void main(String[] args) {int num1 = 10;int num2 = 20;swap(num1, num2);System.out.println("num1 = " + num1);System.out.println("num2 = " + num2);}public static void swap(int a, int b) {int temp = a;a = b;b = temp;System.out.println("a = " + a);System.out.println("b = " + b);}// outputa = 20b = 10num1 = 10num2 = 20

在swap方法中,a、b的值进行交换,并不会影响到 num1、num2。因为,a、b中的值,只是从 num1、num2 的复制过来的。也就是说,a、b相当于num1、num2 的副本,副本的内容无论怎么修改,都不会影响到原件本身。
通过上面例子,我们已经知道了一个方法不能修改一个基本数据类型的参数,而对象引用作为参数就不一样。例二
public static void main(String[] args) {int[] arr = { 1, 2, 3, 4, 5 };System.out.println(arr[0]);change(arr);System.out.println(arr[0]);}public static void change(int[] array) {// 将数组的第一个元素变为0array[0] = 0;}// output10

array 被初始化 arr 的拷贝也就是一个对象的引用,也就是说 array 和 arr 指向的时同一个数组对象。 因此,外部对引用对象的改变会反映到所对应的对象上。
通过 example2 我们已经看到,实现一个改变对象参数状态的方法并不是一件难事。理由很简单,方法得到的是对象引用的拷贝,对象引用及其他的拷贝同时引用同一个对象。
很多程序设计语言(特别是,C++和Pascal)提供了两种参数传递的方式:值调用和引用调用。有些程序员(甚至本书的作者)认为Java程序设计语言对对象采用的是引用调用,实际上,这种理解是不对的。由于这种误解具有一定的普遍性,所以下面给出一个反例来详细地阐述一下这个问题。
- 例三
交换之前:public class Test {public static void main(String[] args) {// TODO Auto-generated method stubStudent s1 = new Student("小张");Student s2 = new Student("小李");Test.swap(s1, s2);System.out.println("s1:" + s1.getName());System.out.println("s2:" + s2.getName());}public static void swap(Student x, Student y) {Student temp = x;x = y;y = temp;System.out.println("x:" + x.getName());System.out.println("y:" + y.getName());}}// outputx:小李y:小张s1:小张s2:小李

交换之后:
通过上面两张图可以很清晰的看出: 方法并没有改变存储在变量 s1 和 s2 中的对象引用。swap方法的参数x和y被初始化为两个对象引用的拷贝,这个方法交换的是这两个拷贝
对象引用
引用传递的精髓:同一块堆内存空间,可以同时被多个栈内存所指向,不同的栈可以修改同一块堆内存的内容。
public class TestDemo {public static void main(String args[]) {Person per1 = new Person() ;per.name = "张三" ;per.age = 30 ;Person per2 = per1 ; // 引用传递per2.name = "李四" ;per1.get();}}
对应的内存分配图如下
再来看另外一个:
public class TestDemo {public static void main(String args[]) {Person per1 = new Person() ;Person per2 = new Person() ;per1.name = "张三" ;per1.age = 20 ;per2.name = "李四" ;per2.age = 30 ;per2 = per1 ;// 引用传递per2.name = "王五" ;per1.tell() ;}}
对应的内存分配图如下:
Java程序设计语言对对象采用的不是引用调用,实际上,对象引用是按值传递的。
下面再总结一下Java中方法参数的使用情况:
- 一个方法不能修改一个基本数据类型的参数(即数值型或布尔型),操作无效
- 一个方法可以改变一个对象参数的状态。
- 一个方法不能让对象参数引用一个新的对象。不会抛出异常,但操作无效。
类变量
成员变量和局部变量的区别
| 区别 | 成员变量 | 局部变量 |
|---|---|---|
| 在类中位置不同 | 类中方法外 | 方法内或者方法声明上 |
| 在内存中位置不同 | 堆内存 | 栈内存 |
| 声明周期不同 | 随着对象存在而存在,随着对象消失而消失 | 随着方法调用而存在,随着方法调用完毕而消失 |
| 初始化值不同 | 有默认的初始值 | 没有默认的初始值,必须先定义,赋值,才能使用 |
注意:局部变量名可以和成员变量名一样,在方法中使用的时候,采用就近原则。
静态变量和成员变量的区别
| 区别 | 静态变量 | 成员变量 |
|---|---|---|
| 所属不同 | 属于类,所以也称为类变量 | 属于对象,所以也称为实例变量(对象变量) |
| 内存中位置不同 | 存储于方法区的静态区 | 存储于堆内存 |
| 内存中出现时间不同 | 随着类的加载而加载,随着类的消失而消失 | 随着对象的创建而存在,随着对象的消失而消失 |
| 调用不同 | 可以通过类名调用,也可以通过对象调用 | 只能通过的对象来调用 |
