一. 对象 VS 对象引用
- 对象:类的一个实例化,存储在堆内存中
- 对象引用:相当于c++中的指针,指向类的实例对象,存储在栈内存中
public class Person{
char name;
int age;
}
Person xiaoming = new Person();
说明:最后一行代码实际上等价于
Person xiaoming;
xiaoming = new Person();
new Person()
就是在堆内存中创建了一个Person类的实例对象,xiaoming
即为一个对象引用,=
不是赋值语句,而是地址传递,让栈内存中的对象引用指向堆内存中的实例对象
二. 对象作为参数传递
java本质上只有按值传递,无论是基本数据类型还是引用类型,当作为参数传递给一个方法时,两种类型都是按值传递的。
-
在Java中,当对象作为参数传递时,实际上传递的是一份“ 引用的拷贝 ”,地址值。
对比以下两例代码:
class Person{
int age;
char name;
Person(int a){
age = a;
}
}
public class Test {
public static void main(String[] args) {
Person xiaoming = new Person(18);
System.out.println(xiaoming.age); // 18
changeAge(xiaoming,20);
System.out.println(xiaoming.age); // 20
}
public static void changeAge(Person p,int a){
p.age = a;
}
}
说明:实参与形参指向同一个实例对象,堆内存中原对象的值改变
public class Test {
public static void main(String[] args) {
Person xiaoming = new Person(18);
System.out.println(xiaoming.age); // 18
changeAge(xiaoming,20);
System.out.println(xiaoming.age);// 18
}
public static void changeAge(Person p,int a){
p = new Person(a);
}
}
说明:堆内存中原对象的值未改变,开辟了新的内存空间,实参与形参指向不同
三. 对象初始化顺序
class Person{
int age;
// int age = 15;
Person(){
System.out.println(age + " constructor");
}
Person(int a){
System.out.println(age + " constructor");
age = a;
}
}
public class Test {
public static void main(String[] args) {
Person xiaoming = new Person();
System.out.println(xiaoming.age);
xiaoming = changeObj();
System.out.println(xiaoming.age);
}
public static Person changeObj(){
Person p = new Person(18);
return p;
}
}
/**
0 constructor // 15 constructor
0 // 15
0 constructor // 15 constructor
18 // 18
*/
结论:
- 先对变量进行默认初始化,如果用户有自定义初值则直接执行
- 最后调用对应构造函数指定初值
四. static
static修饰的数据成员属于类的静态数据成员,存储在类内存区的公共存储单元,可以被任意类的实例对象访问
static变量
-
static 修饰的变量称为类变量或全局变量或成员变量,在类被加载的时候成员变量即被初始化,与类关联,只要类存在,static变量就存在。非static修饰的成员变量是在对象new出来的时候划分存储空间,是与具体的对象绑定的,该成员变量仅为当前对象所拥有的。
-
static修饰的变量在加载的时候先于main方法加载在内存中的数据共享区-------方法区,而非static的变量在加载的时候,是要创建变量才加载在堆内存中的。
-
一个static变量单独划分一块存储空间,不与具体的对象绑定在一起,该存储空间被类的各个对象所共享。static变量值在方法区加载一次,而非static在创建对象时会加载很多次。每次创建都会拷贝一份。
-
对象在引用成员变量是直接通过类名.变量名调用,对象在引用实例变量时只能通过对象名.变量名调用。
-
在类中调用成员变量时直接调用或者以类名.变量名方式调用,实例变量则用this或者直接调用。
static方法
-
static修饰的方法也和static变量一样。先于main方法被加载到方法区,以便共享使用。
-
静态的static方法中不能使用this或者super关键字。非静态的方法是属于对象的,方法里可以用this和super。
-
static方法可以用对象.方法名来调用,也可以用类名.方法名来调用。而非静态的方法只能创建对象后时调用。
-
static方法是加载一次,被所有的对象所共享。而非静态方法是有多少个对象就拷贝多少次,每个对象只能调用自己的拷贝的方法。
-
对象调用非静态的方法时,不考虑线程安全性的问题,而调用静态方法时,要考虑安全性的问题。因为静态方法只有一份。而对象的方法是自己有自己的。
-
同一个类中,静态方法中只能访问类中的静态成员。而非静态方法可以访问静态成员(使用类名调用,或者创创建本类的对象调用)。
使用场景
-
属于类的公有属性
-
需要先初始化加载或者是被多次调用
举例:
class Person{
// Person 的公有属性
static int age;
static String name;
Person(int a,String n){
age = a;
name = n;
}
// Person 的公有方法
static void speak(){
System.out.println("I am "+ name + " and I am "+ age + " years old.");
}
}
public class Test {
public static void main(String[] args) {
Person xiaoming = new Person(18,"xiaoming");
Person.speak(); // I am xiaoming and I am 18 years old.
// xiaoming.speak();
}
}
五. final
-
修饰类、变量和方法,用于表示它修饰的类、方法和变量不可改变。
- 修饰类,类不能被继承
final class Person{ int age = 18; static String name; Person(int a,String n){ age = a; name = n; } } class Teacher extends Person{} // 报错:java: 无法从最终Person进行继承
- 修饰变量,变量为常量
class Person{ final int age = 18; static String name; Person(int a,String n){ age = a; // 报错:java: 无法为最终变量age分配值 name = n; } }
- 修饰方法,方法不能被覆盖
class Person{ int age = 18; String name = "xiaoming"; final public void speak(){ System.out.println("I am "+ name + " and I am "+ age + " years old."); } } class Teacher extends Person{ public void speak(){ // 报错:java: Teacher中的speak()无法覆盖Person中的speak() // 被覆盖的方法为final System.out.println("I am "+ name + " and I am "+ age + " years old.I am a teacher"); } }
六. 数组
一维数组
数组声明
- 声明的仅是引用变量,所以不可以在[]内指定数组大小
int[] arr;
String[] example;
Person[] p;
数组初始化
- 使用new关键字
类型标识符[] 数组名=new 类型标识符[数组长度];
int[] arr = new int[10];
String[] example = new String[10];
Person[] p = new Person[10];
- 直接在声明的时候进行定义及初始化
int[] a = {3,4,5,6};
String[] s = {"ab","bc","cd"};
Person[] p = {new Person("xiaoming"),new Person("xiaowang"),new Person("xiaoli")};
- 采用1和2结合的方法
int[] f4= new int[] { 1, 2, 3, 4};
二维数组
数组声明
int[][] arr;
数组初始化
- 使用new关键字
数组名=new 类型说明符[ 数组长度 ][];
数组名=new 类型说明符[ 数组长度 ][ 数组长度 ];
arr = new int[3][4];
// 等价于
arr = new int[3][];
arr[0] = new int[4];
arr[1] = new int[4];
arr[2] = new int[4];
- 使用指定的初值对数组初始化
int[][] arr1={{3,-9,6},{8,0,1},{11,9,8}};
// 等价于
int[][] arr1=new int[3][];
arr1[0]= new int[]{3,-9, 6};
arr1[1]= new int[]{8,0,1};
arr1[2]= new int[]{11,9,8};
数据元素:基本 vs 引用
- 基本数据类型默认值为0,引用数据类型默认值为null
- 使用引用数据类型,每一个元素都是一个对象引用,指向另一块内存中的对象
public class Test {
public static void main(String[] args) {
int arr1[] = new int[10];
for(int i=0; i < arr1.length; i++){
System.out.printf("%d ",arr1[i]);
}
for(int i=0; i < arr1.length; i++){
arr1[i] = i;
System.out.printf("%d ",arr1[i]);
}
System.out.println();
String arr2[] = new String[10];
for(int i=0; i < arr2.length; i++){
System.out.printf("%s ",arr2[i]);
}
for(int i=0; i < arr2.length; i++){
arr2[i] = new String("ab"+ i);
System.out.printf("%s ",arr2[i]);
}
}
}
/**
0 0 0 0 0 0 0 0 0 0 1 2 3 4 5 6 7 8 9
null null null null null null null null null null ab0 ab1 ab2 ab3 ab4 ab5 ab6 ab7 ab8 ab9
*/
总结
初步学习了Java是如何实现面向对象这一思想的,在学习过程中通过适当类比c++的语法帮助自己更好地掌握了Java类和对象的基础语法知识,但这只是开始,还需要更为深入的学习才能把握Java面向对象的精髓。
在整理博客的过程中参考了一些资料以及许多他人优秀的文章,就不一一列举,在此表示感谢。