类与对象
2121字约7分钟
2025-09-23
创建对象
// 创建对象
// 方法一
// 先声明,所以现在的cat为空(null)
Cat cat;
// 在创建对象,此时将创建的对象赋值给cat
cat = new Cat();
// 方法二
//直接创建,声明和创建一起写
Cat cat = new Cat();
// 创建一个临时对象
// 创建一个临时的Homework对象,并调用该对象中的cound1方法
// cound1运行完之后,该对象如果没有变量在引用,那么该对象则会被系统销毁
new Homework().cound1(); // 输出 10
类与对象属性的默认值
在类与对象中,属性的默认值和该属性数组的默认值是一致的
int a;
int[] b = new int[1];
// 那么属性a的默认值和数组b[0]的默认值是一样的(前提是它们都是同一个数据类型)
- 创建一个对象,则会在堆中生成一个空间
- 调用一个方法,则会在栈中生成一个空间
String
类型的对象会先指向堆中的空间,然后堆中的空间在指向常量池的对应的常量- 数据类型的对象的值则是在堆中
方法内存运行原理图
每个方法在内存中相当于是一个小栈,一个独立的空间
当方法运行完毕之后,该方法会被销毁;
同理当main方法(主方法)运行完毕之后,main方法(主方法)也会被销毁,也就是所谓的程序结束
查看一个对象的类名
Cat cat = new Cat();
System.out.println("cat = " + cat.getClass());
方法的使用细节
class Exercise {
// 该方法中的括号里面的值叫做形参
public void Exercise01(String n, int n1) { }
public int Exercise02(int n1, int n2) {
int a = n1 + n2;
return a;
}
// 方法中不能嵌套方法
/*
错误例子
下面这几句代码是不能运行的
public void Exercise03() {
public void Exercise04() { }
}
*/
}
public class MethodDetail02 {
public static void main(String[] args) {
Exercise p1 = new Exercise();
// 下面这行代码中的括号里面的值叫做实参
// 实参和形参里面的值要相对应
// 无论是顺序还是数据类型都要一致
p1.Exercise01("Hello", 123);
// 错误例子
// p1.Exercise01(123, "Hello");
byte a1 = 6;
byte a2 = 10;
// 这样写也是可以的,a1 和 a2都是 byte类型
// 而方法Exercise02中的返回值类型则是int类型的,所以小转大成立
System.out.println(p1.Exercise02(a1, a2));
// 错误例子1
// double a3 = 2.3;
// double a4 = 4.4;
// 因为方法Exercise02中的返回值类型则是int类型的,而a3和a4都是double类型的,所以大转小不成立
// p1.Exercise02(a3, a4);
// 错误例子2
// 因为方法Exercise02的形参中有两个值,而实参中只有一个
// 这里是把a5赋值给n1,但是n2的值没有被赋值,所以会报错
// p1.Exercise02(a5);
}
}
在Java中,基本数据类型,方法与方法之间传递数值的过程叫做值拷贝,也就是说将实参里的值直接赋值到形参中,所以形参的任何变化不会影响到实参
引用类型之间赋值,只要其中一个引用类型的值发生变化,则另一个引用类型的值也会发生变化;
方法与方法之间传递数值的过程叫做拷贝内存地址,也就是说实参里的值和形参里的值都是同一个内存地址中的,所以形参的任何变化会影响到实参
赋值
class Exercise {
String name;
int age;
}
class ReferenceType {
public void text1(Exercise p) {
// 在null之前赋值
// 由于是引用类型,所以Exercise p中的age值被更改
p.name = "java";
// 该方法中的p为空
p = null;
// 在null之后赋值
// 由于空指针不能赋值,这里则报空指针异常
p.name = "PHP";
}
}
public class Stringwork01 {
public static void main(String[] args) {
ReferenceType type = new ReferenceType();
Exercise p = new Exercise();
p.name = "nihao";
p.age = 10;
// 因为在text1()方法中,p = null,随后p.name = "PHP";
// 所以空指针异常
type.text1(p);
// 因为是引用类型,且 p.name = "java"; 是在 p = null 之前赋值的
// 所以 p.name 的值更改成功,所以这里理应输出 java
// 因为前面的异常报错,所以下面这条语句不会被执行
System.out.println(p.name); // java
}
}
相同类与不同类之间的调用区别
class A {
public void Exercise01() {
System.out.println("Exercise01方法已被调用");
// 同一个类中,不需要创建对象就可以调用同一个类中的方法
Exercise02();
System.out.println("继续执行Exercise01方法");
}
public void Exercise02() {
System.out.println("Exercise02方法已被调用");
// 要调用另一个类中的方法,需要创建对象才能调用
B b = new B();
b.Exercise03(10);
System.out.println("继续执行Exercise02方法");
}
}
class B {
public void Exercise03(int n) {
System.out.println("Exercise03方法已被调用 " + n);
}
}
public class MethodDetail03 {
public static void main(String[] args) {
System.out.println("主程序已运行");
A a = new A();
a.Exercise01();
System.out.println("继续执行main方法");
}
}
// 运行结果如下
/*
主程序已运行
Exercise01方法已被调用
Exercise02方法已被调用
Exercise03方法已被调用 10
继续执行Exercise02方法
继续执行Exercise01方法
继续执行main方法
*/
相互调用注意事项
下面这种调用方式会导致程序进入一个死循环,所以最后程序就会报错
class Person {
public Person(String name, int age) {
Person1 p1 = new Person1();
p1.Demo();
}
}
class Person1 {
public void Demo() {
Person p1 = new Person("a", 9);
System.out.println(p1.name);
}
}
克隆对象
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 = "张三";
System.out.println(t5.name); // 张三
}
}
递归
当一个栈运行完,并且没有数据引用的时候,则该栈运行结束且被销毁
class Exercise {
public void Recursion(int n) {
// 判断 n 是否大于 2
if (n > 2) {
// 如果 n 大于 2 则调用 Recursion 方法,并且 n - 1
// 每调用一次方法都会生成新的栈,直到n不大于2时,则停止调用 Recursion 方法
// 最后一个栈运行完之后就会返回到上一个栈,直到所有栈都运行完成之后在返回到主方法中
Recursion(n - 1);
}
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 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();
// 然后在将 5 赋值给 factorial 方法,所以 factorial 中的n = 5
int num = demo.factorial(5);
System.out.println("num = " + num); // 输出 num = 120
}
}