文章来自Java Guide 用于学习如有侵权,立即删除
正文
public
,private
,static
等修饰符所修饰,而局部变量不能被访问控制修饰符及 static
所修饰;但是,成员变量和局部变量都能被 final
所修饰。static
修饰的,那么这个成员变量是属于类的,如果没有使用 static
修饰,这个成员变量是属于实例的。而对象存在于堆内存,局部变量则存在于栈内存。final
修饰的成员变量也必须显式地赋值),而局部变量则不会自动赋值。为什么成员变量有默认值?
先不考虑变量类型,如果没有默认值会怎样?变量存储的是内存地址对应的任意随机值,程序读取该值运行会出现意外。
默认值有两种设置方式:手动和自动,根据第一点,没有手动赋值一定要自动赋值。成员变量在运行时可借助反射等方法手动赋值,而局部变量不行。
对于编译器(javac)来说,局部变量没赋值很好判断,可以直接报错。而成员变量可能是运行时赋值,无法判断,误报“没默认值”又会影响用户体验,所以采用自动赋默认值。
成员变量与局部变量代码示例:
public class VariableExample {
// 成员变量
private String name;
private int age;
// 方法中的局部变量
public void method() {
int num1 = 10; // 栈中分配的局部变量
String str = "Hello, world!"; // 栈中分配的局部变量
System.out.println(num1);
System.out.println(str);
}
// 带参数的方法中的局部变量
public void method2(int num2) {
int sum = num2 + 10; // 栈中分配的局部变量
System.out.println(sum);
}
// 构造方法中的局部变量
public VariableExample(String name, int age) {
this.name = name; // 对成员变量进行赋值
this.age = age; // 对成员变量进行赋值
int num3 = 20; // 栈中分配的局部变量
String str2 = "Hello, " + this.name + "!"; // 栈中分配的局部变量
System.out.println(num3);
System.out.println(str2);
}
}
静态变量也就是被 static
关键字修饰的变量。它可以被类的所有实例共享,无论一个类创建了多少个对象,它们都共享同一份静态变量。也就是说,静态变量只会被分配一次内存,即使创建多个对象,这样可以节省内存。
静态变量是通过类名来访问的,例如StaticVariableExample.staticVar
(如果被 private
关键字修饰就无法这样访问了)。
public class StaticVariableExample {
// 静态变量
public static int staticVar = 0;
}
通常情况下,静态变量会被 final
关键字修饰成为常量。
public class ConstantVariableExample {
// 常量
public static final int constantVar = 0;
}
?? 注意 char
在 Java 中占两个字节。
字符型常量和字符串常量代码示例:
public class StringExample {
// 字符型常量
public static final char LETTER_A = 'A';
// 字符串常量
public static final String GREETING_MESSAGE = "Hello, world!";
public static void main(String[] args) {
System.out.println("字符型常量占用的字节数为:"+Character.BYTES);
System.out.println("字符串常量占用的字节数为:"+GREETING_MESSAGE.getBytes().length);
}
}
输出:
字符型常量占用的字节数为:2
字符串常量占用的字节数为:13
方法的返回值 是指我们获取到的某个方法体中的代码执行后产生的结果!(前提是该方法可能产生结果)。返回值的作用是接收出结果,使得它可以用于其他的操作!
我们可以按照方法的返回值和参数类型将方法分为下面这几种:
1、无参数无返回值的方法
public void f1() {
//......
}
// 下面这个方法也没有返回值,虽然用到了 return
public void f(int a) {
if (...) {
// 表示结束方法的执行,下方的输出语句不会执行
return;
}
System.out.println(a);
}
2、有参数无返回值的方法
public void f2(Parameter 1, ..., Parameter n) {
//......
}
3、有返回值无参数的方法
public int f3() {
//......
return x;
}
4、有返回值有参数的方法
public int f4(int a, int b) {
return a * b;
}
这个需要结合 JVM 的相关知识,主要原因如下:
public class Example {
// 定义一个字符型常量
public static final char LETTER_A = 'A';
// 定义一个字符串常量
public static final String GREETING_MESSAGE = "Hello, world!";
public static void main(String[] args) {
// 输出字符型常量的值
System.out.println("字符型常量的值为:" + LETTER_A);
// 输出字符串常量的值
System.out.println("字符串常量的值为:" + GREETING_MESSAGE);
}
}
1、调用方式
在外部调用静态方法时,可以使用 类名.方法名
的方式,也可以使用 对象.方法名
的方式,而实例方法只有后面这种方式。也就是说,调用静态方法可以无需创建对象 。
不过,需要注意的是一般不建议使用 对象.方法名
的方式来调用静态方法。这种方式非常容易造成混淆,静态方法不属于类的某个对象而是属于这个类。
因此,一般建议使用 类名.方法名
的方式来调用静态方法。
public class Person {
public void method() {
//......
}
public static void staicMethod(){
//......
}
public static void main(String[] args) {
Person person = new Person();
// 调用实例方法
person.method();
// 调用静态方法
Person.staicMethod()
}
}
2、访问类成员是否存在限制
静态方法在访问本类的成员时,只允许访问静态成员(即静态成员变量和静态方法),不允许访问实例成员(即实例成员变量和实例方法),而实例方法不存在这个限制。
重载就是同样的一个方法能够根据输入数据的不同,做出不同的处理
重写就是当子类继承自父类的相同方法,输入数据一样,但要做出有别于父类的响应时,你就要覆盖父类方法
发生在同一个类中(或者父类和子类之间),方法名必须相同,参数类型不同、个数不同、顺序不同,方法返回值和访问修饰符可以不同。
《Java 核心技术》这本书是这样介绍重载的:
如果多个方法(比如
StringBuilder
的构造方法)有相同的名字、不同的参数, 便产生了重载。StringBuilder sb = new StringBuilder(); StringBuilder sb2 = new StringBuilder("HelloWorld");
编译器必须挑选出具体执行哪个方法,它通过用各个方法给出的参数类型与特定方法调用所使用的值类型进行匹配来挑选出相应的方法。 如果编译器找不到匹配的参数, 就会产生编译时错误, 因为根本不存在匹配, 或者没有一个比其他的更好(这个过程被称为重载解析(overloading resolution))。
Java 允许重载任何方法, 而不只是构造器方法。
综上:重载就是同一个类中多个同名方法根据不同的传参来执行不同的逻辑处理。
重写发生在运行期,是子类对父类的允许访问的方法的实现过程进行重新编写。
private/final/static
则子类就不能重写该方法,但是被 static
修饰的方法能够被再次声明。综上:重写就是子类对父类方法的重新改造,外部样子不能改变,内部逻辑可以改变。
区别点 | 重载方法 | 重写方法 |
---|---|---|
发生范围 | 同一个类 | 子类 |
参数列表 | 必须修改 | 一定不能修改 |
返回类型 | 可修改 | 子类方法返回值类型应比父类方法返回值类型更小或相等 |
异常 | 可修改 | 子类方法声明抛出的异常类应比父类方法声明抛出的异常类更小或相等; |
访问修饰符 | 可修改 | 一定不能做更严格的限制(可以降低限制) |
发生阶段 | 编译期 | 运行期 |
方法的重写要遵循“两同两小一大”(以下内容摘录自《疯狂 Java 讲义》,issue#892 ):
?? 关于 重写的返回值类型 这里需要额外多说明一下,上面的表述不太清晰准确:如果方法的返回类型是 void 和基本数据类型,则返回值重写时不可修改。但是如果方法的返回值是引用类型,重写时是可以返回该引用类型的子类的。
public class Hero {
public String name() {
return "超级英雄";
}
}
public class SuperMan extends Hero{
@Override
public String name() {
return "超人";
}
public Hero hero() {
return new Hero();
}
}
public class SuperSuperMan extends SuperMan {
public String name() {
return "超级超级英雄";
}
@Override
public SuperMan hero() {
return new SuperMan();
}
}
从 Java5 开始,Java 支持定义可变长参数,所谓可变长参数就是允许在调用方法时传入不定长度的参数。就比如下面的这个 printVariable
方法就可以接受 0 个或者多个参数。
public static void method1(String... args) {
//......
}
另外,可变参数只能作为函数的最后一个参数,但其前面可以有也可以没有任何其他参数。
public static void method2(String arg1, String... args) {
//......
}
遇到方法重载的情况怎么办呢?会优先匹配固定参数还是可变参数的方法呢?
答案是会优先匹配固定参数的方法,因为固定参数的方法匹配度更高。
我们通过下面这个例子来证明一下。
public class VariableLengthArgument {
public static void printVariable(String... args) {
for (String s : args) {
System.out.println(s);
}
}
public static void printVariable(String arg1, String arg2) {
System.out.println(arg1 + arg2);
}
public static void main(String[] args) {
printVariable("a", "b");
printVariable("a", "b", "c", "d");
}
}
输出:
ab
a
b
c
d
另外,Java 的可变参数编译后实际会被转换成一个数组,我们看编译后生成的 class
文件就可以看出来了。
public class VariableLengthArgument {
public static void printVariable(String... args) {
String[] var1 = args;
int var2 = args.length;
for(int var3 = 0; var3 < var2; ++var3) {
String s = var1[var3];
System.out.println(s);
}
}
// ......
}
大家好,我是xwhking,一名技术爱好者,目前正在全力学习 Java,前端也会一点,如果你有任何疑问请你评论,或者可以加我QQ(2837468248)说明来意!希望能够与你共同进步