类与对象
2886字约10分钟
2024-12-02
类与对象
- 类是抽象的,概念的,代表一类事物的
- 对象(也就是成员方法)是具体的,实际的,代表一个具体的事物
- 类是对象的模板,对象是类的一个个体,对应一个实例
类与对象的内存运行原理图
cat 只是引用对象而已(也就是引用 Cat 对象),真正的对象其实是在堆和方法区里面,也就是0x0011
和常量池
属性 / 成员变量的使用细节
属性可以是 引用数据类型,可以是 基本数据类型,也可以是 一个数组 或则 另一个对象
属性的默认值
在类与对象中,属性的默认值和该属性数组的默认值是一致的
int a;
int[] b = new int[1];
// 那么属性a的默认值和数组b[0]的默认值是一样的(前提是它们都是同一个数据类型)
创建对象
// 创建对象
// 方法一
// 先声明,所以现在的cat为空(null)
Cat cat;
// 在创建对象,此时将创建的对象赋值给cat
cat = new Cat();
// 方法二
//直接创建,声明和创建一起写
Cat cat = new Cat();
查看一个对象的类名
Cat cat = new Cat();
System.out.println("cat = " + cat.getClass());
创建临时对象
// 创建一个临时对象
// 创建一个临时的Homework对象,并调用该对象中的cound1方法
// cound1运行完之后,该对象如果没有变量在引用,那么该对象则会被系统销毁
new Homework().cound1(); // 输出 10
类与对象赋值分析
上图中,p1赋值给p2,也可以说是p2指向p1
方法
方法写好之后,如果不去调用(使用),方法里面的类容则不会输出
要先创建对象之后在调用方法
方法内存运行原理图
每个方法在内存中相当于是一个小栈,一个独立的空间
当方法运行完毕之后,该方法会被销毁;同理当main方法(主方法)运行完毕之后,main方法(主方法)也会被销毁,也就是所谓的程序结束
成员方法的好处:
- 提高代码的复用性
- 可以将实现的细节封装起来,然后供其他用户来调用即可
方法的使用事项和细节
方法中形参列表中的使用细节以及注意事项
成员方法与成员方法之间的调用细节(相同类与不同类之间的区别)
对象名也可以叫做方法名
在主类中调用主类里的方法,需要创建主类的对象才能调用;在别的类中只要是同一个类中的方法都可以直接调用,而无需创建本类的对象。
方法的传递机制
方法中基本数据类型赋值机制
class swap {
public void swap1(int a, int b) {
System.out.println("交换前a = " + a + ", b = " + b); //a = 10, b = 20
int num = a;
a = b;
b = num;
System.out.println("交换后a = " + a + ", b = " + b); //a = 20, b = 10
}
}
public class MethodExercise03 {
public static void main(String[] args) {
int a = 10;
int b = 20;
swap a1 = new swap();
a1.swap1(a, b);
// 形参变化不会影响到实参,这里的a和b是输出的是主方法中的a和b,而不是swap1方法中的a和b
System.out.println("原值a = " + a + ", b = " + b); //a = 10, b = 20
}
}
总结:在Java中,基本数据类型,方法与方法之间传递数值的过程叫做值拷贝,也就是说将实参里的值直接赋值到形参中,所以形参中的任何变化不会影响到实参
也就是说基本数据类型之间赋值,只是互相拷贝而已,另一个值的变化不会影响到本来的值
方法中引用类型赋值机制
class ReferenceType {
public void ReferenceType1(int[] arr) {
// 这里修改了arr数组中的第一个元素的值
arr[0] = 200;
System.out.print("ReferenceType1:");
for (int i = 0; i < arr.length; i++) {
System.out.print(arr[i] + " "); //输出 200 2 3
}
}
}
public class MethodExercise04 {
public static void main(String[] args) {
int[] arr = {1, 2, 3};
ReferenceType type = new ReferenceType();
type.ReferenceType1(arr);
System.out.print("\nmain: ");
for (int i = 0; i < arr.length; i++) {
System.out.print(arr[i] + " "); //输出为 200 2 3
}
}
}
总结:引用类型之间赋值,只要其中一个引用类型的值发生变化,则另一个引用类型的值也会发生变化;
方法与方法之间传递数值的过程叫做拷贝内存地址,也就是说实参里的值和形参里的值都是同一个内存地址中的,所以形参的任何变化会影响到实参
数组和String都是引用类型的
方法与方法之间赋值
class Exercise {
String name;
int age;
}
class ReferenceType {
public void text1(Exercise p) {
p.age = 10000;
}
}
public class MethodExercise04 {
public static void main(String[] args) {
ReferenceType type = new ReferenceType();
Exercise p = new Exercise();
p.name = "nihao";
p.age = 10;
// 因为上面已经创建了对象(Exercise p = new Exercise();),所以这里就相当于把该对象赋值给text1中的 (Exercise p) 里面
type.text1(p);
System.out.println("main : p.age = " + p.age); // p.age = 10000
}
}
总结:引用类型之间赋值,只要其中一个引用类型的值发生变化,则另一个引用类型的值也会发生变化;
所以这里方法与方法之间传递数值的过程叫做拷贝内存地址,也就是说实参里的值和形参里的值都是同一个内存地址中的,所以形参的任何变化会影响到实参
注意点1
class Exercise {
String name;
int age;
}
class ReferenceType {
// new Exercise(); 传进来之后
// 这里的 p = new Exercise();
public void text1(Exercise p) {
// 但是这里 p = null 了
// 所以这里的 p 从原本的new Exercise(); 变成了 null
// 但并不影响主方法里的 Exercise p = new Exercise();
// 这里只是改变的是 public void text1(Exercise p) 里的 p 值
p = null;
}
}
public class MethodExercise04 {
public static void main(String[] args) {
ReferenceType type = new ReferenceType();
Exercise p = new Exercise();
p.name = "nihao";
p.age = 10;
// 因为 p = new Exercise();
// 所以这里是将 new Exercise(); 传到 public void text1(Exercise p) 里的 p 中
type.text1(p);
// 所以 p.age = 10 (这里的 p.age 是 Exercise p = new Exercise(); 里的 p )
System.out.println("main : p.age = " + p.age); // p.age = 10
}
}
注意点2
class Exercise {
String name;
int age;
}
class ReferenceType {
public void text1(Exercise p) {
// 这里是新对象,所以并不会影响到原来对象里的值
p = new Exercise();
p.name = "hello";
p.age = 99;
}
}
public class MethodExercise04 {
public static void main(String[] args) {
ReferenceType type = new ReferenceType();
Exercise p = new Exercise();
p.name = "nihao";
p.age = 10;
type.text1(p);
// 这里的 p.age 是老对象里的值
System.out.println("main : p.age = " + p.age); // p.age = 10
}
}
克隆对象
class text {
String name;
int age;
}
class text1 {
public text demo(text t3) {
text t1 = new text();
// t1 = t3; 如果这样写就会导致,t1的值发生改变则t3的值也会发生改变,所以要一个一个的赋值
t1.name = t3.name;
t1.age = t3.age;
return t1;
}
}
public class MethodExercise07 {
public static void main(String[] args) {
text t3 = new text();
t3.name = "Hello,World!";
t3.age = 666;
// 创建一个对象
text1 t4 = new text1();
// 将对象 t3 赋值给 t4.demo(t3) 方法
text t5 = t4.demo(t3);
t5.name = "ssh";
System.out.println("t3.name = " + t3.name); // 输出为 Hello,World!
System.out.println("t5.name = " + t5.name); // 输出为 ssh
// 新对象 t6 调用 t4.demo(t3) ,再次复制对象从而不影响原来的对象
text t6 = t4.demo(t3);
t6.name = "张三";
System.out.println("\nt3 = " + t3.name); // 输出为 Hello,World!
System.out.println("t6 = " + t6.name); // 输出为 张三
System.out.println("t5.name = " + t5.name); // 输出为 ssh
}
}
方法递归调用
当一个栈运行完,并且没有数据引用的时候,则该栈运行结束且被销毁
class Exercise {
public void Recursion(int n) {
// 判断 n 是否大于 2
if (n > 2) {
// 如果 n 大于 2 则调用 Recursion 方法,并且 n - 1
// 每调用一次方法都会生成新的栈,直到n不大于2时,则停止调用 Recursion 方法
// 最后一个栈运行完之后就会返回到上一个栈,直到所有栈都运行完成之后在返回到主方法中
Recursion(n - 1);
}
// 当n = 2时,不在继续递归,则是继续执行if语句下面的代码,if语句下面的代码运行完之后则是返回到上一个方法体中继续执行if语句下面的代码,后面的代码以此类推
System.out.println("n = " + n);
}
}
public class RecursionExercise01 {
public static void main(String[] args) {
// 调用 Exercise类
Exercise demo = new Exercise();
// 然后在将 4 赋值给 Recursion 方法,所以 Recursion 中的n = 4
demo.Recursion(4);
}
}
// 运行结果
n = 2
n = 3
n = 4
注意点
class Exercise {
public void Recursion(int n) {
// 判断 n 是否大于 2
if (n > 2) {
// 如果 n 大于 2 则调用 Recursion 方法,并且 n - 1
// 每调用一次方法都会生成新的栈,直到n不大于2时,则停止调用 Recursion 方法
// 最后一个栈运行完之后就会接着返回到上一个栈,直到所有栈都运行完成之后在返回到主方法中
Recursion(n - 1);
} else {
// 只有当n不大于2时,才会输出下面这段代码,n大于2时则不输出
System.out.println("n = " + n);
}
}
}
public class RecursionExercise01 {
public static void main(String[] args) {
// 调用 Exercise类
Exercise demo = new Exercise();
// 然后在将 4 赋值给 Recursion 方法,所以 Recursion 中的n = 4
demo.Recursion(4);
}
}
// 运行结果
n = 2
方法阶乘
当一个栈运行完,并且没有数据引用的时候,则该栈运行结束且被销毁
class Exercise {
// 阶乘
public int factorial(int n) {
if (n == 1) {
// 当 n = 1时,结束调用 factorial 方法
// 并且返回 1 到上一个栈中,上一个栈在返回到上上一个栈,直到所有栈运行完毕之后,最后的结果才返回到主方法中
return 1;
} else {
// 当n不等于1时,执行下面这段代码
// 然后执行 (n - 1) * n ,得到的结果在返回到 factorial 方法中
// 这里是先执行小括号里面的运算,也就是(n - 1) ,乘以n是最后返回的时候才开始运行
// 注意:return factorial(n - 1) * n; 这样写会导致n的值越来越大
return factorial(n - 1) * n;
}
}
}
public class RecursionExercise01 {
public static void main(String[] args) {
// 调用 Exercise类
Exercise demo = new Exercise();
// 然后在将 4 赋值给 factorial 方法,所以 factorial 中的n = 4
int num = demo.factorial(5);
System.out.println("num = " + num); // 输出 num = 120
}
}