--- title: "05-变量与方法" aliases: - "变量与方法" created: 2025-12-24 --- # 变量与方法 --- ## 一、变量详解 ### **1.1 变量分类** ```drawio ``` Java ```java public class VariableDemo { // ═══════════════════════════════════════════════════════════════════ // 类变量(静态变量):属于类,所有实例共享 // ═══════════════════════════════════════════════════════════════════ private static int classCount = 0; // 类变量 public static final String APP_NAME = "MyApp"; // 静态常量 // ═══════════════════════════════════════════════════════════════════ // 成员变量(实例变量):属于对象,每个实例独立 // ═══════════════════════════════════════════════════════════════════ private String name; // 实例变量 private int age; // 实例变量 public void method(int param) { // param 是局部变量 // ═══════════════════════════════════════════════════════════════ // 局部变量:定义在方法内部 // ═══════════════════════════════════════════════════════════════ int localVar = 10; // 局部变量 String message; // 局部变量(未初始化,使用前必须赋值) for (int i = 0; i < 10; i++) { // i 也是局部变量 // ... } // 代码块内的局部变量 { int blockVar = 20; // 作用域仅限于这个代码块 } // blockVar 在这里不可见 } } ``` ### **1.2 三种变量详细对比** ```drawio ``` ### **1.3 变量存储位置图解** ```drawio ``` ### **1.4 为什么成员变量有默认值** ```drawio ``` ```java public class DefaultValueDemo { // 成员变量有默认值 private int count; // 默认 0 private String name; // 默认 null private boolean active; // 默认 false private int[] array; // 默认 null public void showDefaults() { System.out.println("count: " + count); // 0 System.out.println("name: " + name); // null System.out.println("active: " + active); // false System.out.println("array: " + array); // null } public void localVariableDemo() { int localVar; // System.out.println(localVar); // ❌ 编译错误:变量未初始化 localVar = 10; // 必须先赋值 System.out.println(localVar); // ✅ 正确 } } ``` ### **1.5 静态变量的作用** Java ```java public class Counter { // 静态变量:所有实例共享 private static int totalCount = 0; // 实例变量:每个实例独立 private int instanceId; public Counter() { totalCount++; instanceId = totalCount; } public static void main(String[] args) { Counter c1 = new Counter(); Counter c2 = new Counter(); Counter c3 = new Counter(); System.out.println("c1.instanceId: " + c1.instanceId); // 1 System.out.println("c2.instanceId: " + c2.instanceId); // 2 System.out.println("c3.instanceId: " + c3.instanceId); // 3 // 静态变量通过类名访问 System.out.println("totalCount: " + Counter.totalCount); // 3 } } ``` ```drawio ``` --- --- ## 二、方法详解 ### **2.1 方法的分类** ```drawio ``` ```java public class MethodDemo { private int count = 0; // ═══════════════════════════════════════════════════════════════════ // 1. 无参无返回值 // ═══════════════════════════════════════════════════════════════════ public void printHello() { System.out.println("Hello"); } // ═══════════════════════════════════════════════════════════════════ // 2. 有参无返回值 // ═══════════════════════════════════════════════════════════════════ public void printMessage(String message) { System.out.println(message); } // ═══════════════════════════════════════════════════════════════════ // 3. 无参有返回值 // ═══════════════════════════════════════════════════════════════════ public int getCount() { return count; } // ═══════════════════════════════════════════════════════════════════ // 4. 有参有返回值 // ═══════════════════════════════════════════════════════════════════ public int add(int a, int b) { return a + b; } // ═══════════════════════════════════════════════════════════════════ // 5. 静态方法 // ═══════════════════════════════════════════════════════════════════ public static int max(int a, int b) { return a > b ? a : b; } } ``` ### **2.2 静态方法 vs 实例方法** ```drawio ``` ```java public class MethodAccessDemo { private int instanceVar = 10; // 实例变量 private static int staticVar = 20; // 静态变量 // ═══════════════════════════════════════════════════════════════════ // 静态方法 // ═══════════════════════════════════════════════════════════════════ public static void staticMethod() { System.out.println(staticVar); // ✅ 可以访问静态变量 // System.out.println(instanceVar); // ❌ 不能访问实例变量 // System.out.println(this.instanceVar); // ❌ 不能使用 this anotherStaticMethod(); // ✅ 可以调用静态方法 // instanceMethod(); // ❌ 不能调用实例方法 } public static void anotherStaticMethod() { System.out.println("另一个静态方法"); } // ═══════════════════════════════════════════════════════════════════ // 实例方法 // ═══════════════════════════════════════════════════════════════════ public void instanceMethod() { System.out.println(instanceVar); // ✅ 可以访问实例变量 System.out.println(staticVar); // ✅ 可以访问静态变量 System.out.println(this.instanceVar); // ✅ 可以使用 this staticMethod(); // ✅ 可以调用静态方法 anotherInstanceMethod(); // ✅ 可以调用实例方法 } public void anotherInstanceMethod() { System.out.println("另一个实例方法"); } public static void main(String[] args) { // 静态方法调用 MethodAccessDemo.staticMethod(); // 推荐:类名.方法名 staticMethod(); // 同一个类中可省略类名 // 实例方法调用 MethodAccessDemo demo = new MethodAccessDemo(); demo.instanceMethod(); // 必须通过对象调用 } } ``` **为什么静态方法不能访问非静态成员?** ```drawio ``` ### **2.3 方法重载(Overload)** ```drawio ``` ```java public class OverloadDemo { // ═══════════════════════════════════════════════════════════════════ // ✅ 正确的重载 // ═══════════════════════════════════════════════════════════════════ // 参数个数不同 public int add(int a, int b) { return a + b; } public int add(int a, int b, int c) { return a + b + c; } // 参数类型不同 public double add(double a, double b) { return a + b; } public String add(String a, String b) { return a + b; } // ═══════════════════════════════════════════════════════════════════ // ❌ 错误的重载 // ═══════════════════════════════════════════════════════════════════ // 只有返回值不同,不构成重载 // public double add(int a, int b) { // ❌ 编译错误 // return a + b; // } // 只有参数名不同,不构成重载 // public int add(int x, int y) { // ❌ 编译错误 // return x + y; // } public static void main(String[] args) { OverloadDemo demo = new OverloadDemo(); // 编译器根据参数类型和个数选择合适的方法 System.out.println(demo.add(1, 2)); // 调用 add(int, int) System.out.println(demo.add(1, 2, 3)); // 调用 add(int, int, int) System.out.println(demo.add(1.5, 2.5)); // 调用 add(double, double) System.out.println(demo.add("Hello", "!")); // 调用 add(String, String) } } ``` **重载的编译期绑定**: ```drawio ``` ### **2.4 方法重写(Override)** ```drawio ``` ```java class Animal { protected String name; public void eat() { System.out.println("动物吃东西"); } public Animal getAnimal() { return new Animal(); } // private 方法不能被重写 private void privateMethod() { System.out.println("私有方法"); } // final 方法不能被重写 public final void finalMethod() { System.out.println("final方法"); } // static 方法不是重写,是隐藏 public static void staticMethod() { System.out.println("Animal的静态方法"); } } class Dog extends Animal { // ═══════════════════════════════════════════════════════════════════ // ✅ 正确的重写 // ═══════════════════════════════════════════════════════════════════ @Override public void eat() { // 方法名、参数列表相同 System.out.println("狗吃骨头"); } // 协变返回类型:返回值可以是父类返回值的子类 @Override public Dog getAnimal() { // 返回 Dog 是 Animal 的子类 return new Dog(); } // ═══════════════════════════════════════════════════════════════════ // ❌ 错误的重写 // ═══════════════════════════════════════════════════════════════════ // 不能缩小访问权限 // @Override // void eat() { } // ❌ default < protected // 不能重写 final 方法 // @Override // public void finalMethod() { } // ❌ 编译错误 // static 方法是隐藏,不是重写 public static void staticMethod() { // 没有 @Override System.out.println("Dog的静态方法"); } } ``` ```drawio ``` ### **2.5 可变参数(Varargs)** ```drawio ``` ```java public class VarargsDemo { // ═══════════════════════════════════════════════════════════════════ // 可变参数方法 // ═══════════════════════════════════════════════════════════════════ public int sum(int... numbers) { // numbers 本质是 int[] 数组 int total = 0; for (int num : numbers) { total += num; } return total; } // 可变参数必须在最后 public void print(String prefix, String... messages) { for (String msg : messages) { System.out.println(prefix + msg); } } // ❌ 错误:可变参数不是最后一个 // public void wrong(String... messages, String suffix) { } // ❌ 错误:多个可变参数 // public void wrong(String... a, int... b) { } public static void main(String[] args) { VarargsDemo demo = new VarargsDemo(); // 可变参数的多种调用方式 System.out.println(demo.sum()); // 0 个参数,结果:0 System.out.println(demo.sum(1)); // 1 个参数,结果:1 System.out.println(demo.sum(1, 2, 3)); // 3 个参数,结果:6 System.out.println(demo.sum(1, 2, 3, 4, 5)); // 5 个参数,结果:15 // 也可以直接传入数组 int[] nums = {1, 2, 3, 4, 5}; System.out.println(demo.sum(nums)); // 传入数组,结果:15 } } ``` ```drawio ``` ### **2.6 参数传递机制** ```drawio ``` ```java public class ParameterPassDemo { public static void main(String[] args) { // ═══════════════════════════════════════════════════════════════ // 1. 基本类型传递 // ═══════════════════════════════════════════════════════════════ int num = 10; changeValue(num); System.out.println("num = " + num); // 还是 10,未改变 // ═══════════════════════════════════════════════════════════════ // 2. 引用类型传递 - 修改对象内容 // ═══════════════════════════════════════════════════════════════ int[] arr = {1, 2, 3}; changeArray(arr); System.out.println("arr[0] = " + arr[0]); // 变成 100 // ═══════════════════════════════════════════════════════════════ // 3. 引用类型传递 - 试图改变引用指向 // ═══════════════════════════════════════════════════════════════ int[] arr2 = {1, 2, 3}; changeReference(arr2); System.out.println("arr2[0] = " + arr2[0]); // 还是 1,引用未改变 // ═══════════════════════════════════════════════════════════════ // 4. String 的特殊性 // ═══════════════════════════════════════════════════════════════ String str = "Hello"; changeString(str); System.out.println("str = " + str); // 还是 "Hello" } // 基本类型:修改的是副本 public static void changeValue(int n) { n = 100; // 只修改了副本 } // 引用类型:可以通过引用修改对象内容 public static void changeArray(int[] array) { array[0] = 100; // 修改了原对象的内容 } // 引用类型:不能改变原引用的指向 public static void changeReference(int[] array) { array = new int[]{100, 200, 300}; // 只改变了副本的指向 } // String 是不可变的 public static void changeString(String s) { s = "World"; // 创建了新对象,原引用不变 } } ``` ```drawio ``` ### **2.7 递归方法** ```drawio ``` ```java public class RecursionDemo { // ═══════════════════════════════════════════════════════════════════ // 1. 阶乘:n! = n × (n-1)! // ═══════════════════════════════════════════════════════════════════ public static long factorial(int n) { // 递归出口 if (n <= 1) { return 1; } // 递归体 return n * factorial(n - 1); } // ═══════════════════════════════════════════════════════════════════ // 2. 斐波那契数列:F(n) = F(n-1) + F(n-2) // ═══════════════════════════════════════════════════════════════════ public static long fibonacci(int n) { // 递归出口 if (n <= 1) { return n; } // 递归体(效率较低,有重复计算) return fibonacci(n - 1) + fibonacci(n - 2); } // 优化版:使用记忆化 public static long fibonacciOptimized(int n, long[] memo) { if (n <= 1) { return n; } if (memo[n] != 0) { return memo[n]; // 已计算过,直接返回 } memo[n] = fibonacciOptimized(n - 1, memo) + fibonacciOptimized(n - 2, memo); return memo[n]; } // ═══════════════════════════════════════════════════════════════════ // 3. 二分查找 // ═══════════════════════════════════════════════════════════════════ public static int binarySearch(int[] arr, int target, int left, int right) { // 递归出口 if (left > right) { return -1; // 未找到 } int mid = left + (right - left) / 2; if (arr[mid] == target) { return mid; // 找到了 } else if (arr[mid] > target) { return binarySearch(arr, target, left, mid - 1); } else { return binarySearch(arr, target, mid + 1, right); } } public static void main(String[] args) { System.out.println("5! = " + factorial(5)); // 120 System.out.println("fib(10) = " + fibonacci(10)); // 55 int[] arr = {1, 3, 5, 7, 9, 11, 13}; System.out.println("index of 7: " + binarySearch(arr, 7, 0, arr.length - 1)); // 3 } } ``` ```drawio ``` --- ## 三、构造方法 ### **3.1 构造方法基础** ```drawio ``` ```java public class Person { private String name; private int age; // ═══════════════════════════════════════════════════════════════════ // 无参构造方法 // ═══════════════════════════════════════════════════════════════════ public Person() { this.name = "Unknown"; this.age = 0; System.out.println("无参构造方法被调用"); } // ═══════════════════════════════════════════════════════════════════ // 有参构造方法 // ═══════════════════════════════════════════════════════════════════ public Person(String name) { this.name = name; this.age = 0; System.out.println("一个参数的构造方法被调用"); } // ═══════════════════════════════════════════════════════════════════ // 全参构造方法 // ═══════════════════════════════════════════════════════════════════ public Person(String name, int age) { this.name = name; this.age = age; System.out.println("两个参数的构造方法被调用"); } // ═══════════════════════════════════════════════════════════════════ // 构造方法调用另一个构造方法(this()) // ═══════════════════════════════════════════════════════════════════ public Person(String name, int age, String ignored) { this(name, age); // 调用两个参数的构造方法,必须在第一行 // 其他初始化代码... } public static void main(String[] args) { Person p1 = new Person(); // 无参构造 Person p2 = new Person("Tom"); // 一个参数 Person p3 = new Person("Jerry", 20); // 两个参数 } } ``` ### **3.2 构造方法与继承** ```drawio ``` ```java class Parent { private String name; static { System.out.println("1. 父类静态代码块"); } { System.out.println("3. 父类实例代码块"); } public Parent() { System.out.println("4. 父类无参构造方法"); } public Parent(String name) { this.name = name; System.out.println("4. 父类有参构造方法"); } } class Child extends Parent { private int age; static { System.out.println("2. 子类静态代码块"); } { System.out.println("5. 子类实例代码块"); } public Child() { // 隐式调用 super(); System.out.println("6. 子类无参构造方法"); } public Child(String name, int age) { super(name); // 显式调用父类有参构造方法 this.age = age; System.out.println("6. 子类有参构造方法"); } } // 运行 new Child() 输出: // 1. 父类静态代码块 // 2. 子类静态代码块 // 3. 父类实例代码块 // 4. 父类无参构造方法 // 5. 子类实例代码块 // 6. 子类无参构造方法 ``` --- ## 四、this 与 super 关键字 ```drawio ``` ```java class Animal { protected String name = "Animal"; public void eat() { System.out.println("Animal eating"); } } class Dog extends Animal { private String name = "Dog"; // 同名成员变量 public void eat() { System.out.println("Dog eating"); } public void demo() { // ═══════════════════════════════════════════════════════════════ // this 的使用 // ═══════════════════════════════════════════════════════════════ System.out.println(this.name); // "Dog" - 当前类的 name this.eat(); // "Dog eating" - 当前类的方法 // ═══════════════════════════════════════════════════════════════ // super 的使用 // ═══════════════════════════════════════════════════════════════ System.out.println(super.name); // "Animal" - 父类的 name super.eat(); // "Animal eating" - 父类的方法 } public void showThis() { // this 可以作为参数传递,表示当前对象 printObject(this); } public void printObject(Dog d) { System.out.println(d.name); } } ``` --- ## 五、方法调用的内存过程 ```drawio ``` ```drawio ``` --- ## 六、常见面试题 ```drawio ``` ```java // 经典面试题:下面代码输出什么? public class Test { public static void main(String[] args) { String str = "hello"; change(str); System.out.println(str); // 输出:hello StringBuilder sb = new StringBuilder("hello"); change(sb); System.out.println(sb); // 输出:helloworld } public static void change(String s) { s = "world"; // 新对象,不影响原引用 } public static void change(StringBuilder sb) { sb.append("world"); // 修改原对象内容 } } ```