作用域
https://www.liaoxuefeng.com/wiki/1252599548343744/1260466215676512
作用域 顾名思义是对象在工程中所能生效或是起作用的范围,也表示该对象能够被谁所访问。
关键字
在对象(变量、方法、类)被创建时,使用关键字来表明该对象的作用域,其中包括 public
, protected
, private
, package
。
public
由 public
修饰的对象,表明它可以被任意其他的对象所访问,类似于777权限。
例如一个由 public
修饰的 class
:
package abc;
public class Hello {
public void hi() {
}
}
对于任意的一个 class
都可以访问:
package xyz;
class Main {
void foo() {
// Main可以访问Hello
abc.Hello h = new abc.Hello();
}
}
ps:这里的 package
不一样是为了说明,即使在不同的包里,也依旧可以访问到 public
修饰的类。
同理,对于 public
修饰的 field
和 method
也可以被任意其他对象访问,但是前提是其他对象能够访问 filed
和 method
所在的类。
package xyz;
class Main {
void foo() {
// Main可以访问Hello
abc.Hello h = new abc.Hello();
// hi方法也是可以被访问的,前提是能够访问hi所在的Hello类
h.hi();
}
}
如果不确定是否需要 public
,就不声明为 public
,即尽可能少地暴露对外的字段和方法。
一个.java文件只能包含一个 public
类,但可以包含多个非 public
类。如果有 public
类,文件名必须和 public
类的名字相同。(我不相同好像也没报错。。。)
private
由 private
修饰的 field
和 method
无法被当前类之外的其他对象所访问,也就是说只能被当前所在类里的其它对象所访问,也无法被子类继承。
package abc;
public class Hello {
public void hello() {
//同一个类的其他方法可以访问private
this.hi()
}
// 无法被Hello类外的对象访问
private void hi() {
}
}
在书写方面,我们最好先写 public
修饰的方法,因为 public
定义了类对外的功能,要更为重要。
此外,由于Java的类定义允许嵌套,即一个类的内部允许再定义类。嵌套类也属于当前类的范围,所以也可以访问 private
修饰的对象。
public class Main {
public static void main(String[] args) {
Inner i = new Inner();
i.hi;
}
// private 方法
private static void hello() {
System.out.println("Hello World");
}
// 静态内部类
public static class Inner {
public void hi() {
//静态方法属于类,直接由类名调用
Main.hello();
}
}
}
这里引出另外一个概念,由 static
修饰的内部类被称为嵌套类,而未用 static
修饰的内部类就称为内部类。
对于上面的代码,需要值得注意的一点是:在静态方法中调用非静态方法需要先创建实例,再由实例调用方法,否则编译就不通过。
错误: 无法从静态上下文中引用非静态
这是由于 static
修饰的对象在所在 class
被加载时就完成了初始化,开辟了内存区域可以被直接使用,而非静态的方法则在实例被创建时完成初始化,未创建实例则无法调用非静态方法。
- 如果全部采用非静态方法和非静态类:
public class Main {
// 静态main函数
public static void main(String[] args) {
//调用非静态内部类Inner
Main m = new Main();
Inner i = m.new Inner();
i.hi();
}
// 非静态方法:
private void hello() {
System.out.println("private hello!");
}
// 非静态内部类:
public class Inner {
public void hi() {
// 调用非静态方法
Main m = new Main();
m.hello();
}
}
}
- 如果全部采用静态方法和静态类:
public class Main {
public static void main(String[] args) {
// class中的静态类的静态方法
Main.Inner.hi();
}
// 静态private方法:
private static void hello() {
System.out.println("private hello!");
}
// 静态内部类:
static class Inner {
// 静态类的静态方法
public static void hi() {
Main.hello();
}
}
}
protected
如果不想class内的对象被其他类访问,同时又希望它能够被子类和子类的子类访问,那么就需要使用 protected
进行修饰。
package abc;
public class Hello {
// protected方法:
protected void hi() {
}
}
package xyz;
class Main extends Hello {
void foo() {
Hello h = new Hello();
// 可以访问protected方法:
h.hi();
}
}
package
同一个包内(可能是不同的文件)的类可以访问其他没有被 public
, protected
, private
关键字修饰的对象。
package abc;
class Hello {
void hi() {
}
}
package abc;
public class Main() {
public static void main(String[] args) {
// 可以访问Hello类
Hello h = new Hello();
//可以调用hi方法
h.hi();
}
}
把方法定义为 package
权限有助于测试,因为测试类和被测试类只要位于同一个 package
,测试代码就可以访问被测试类的 package
权限方法。(也就是如果不是必要,最好不加关键字限定作用域?)
局部变量
局部变量是值在方法内定义的变量,生命周期由被创建开始到该方法执行完成。
- 生命周期不同的变量允许有相同的命名。
- 尽可能延后声明局部变量,以缩短它的生命周期。
package abc;
public class Hello {
void hi(String name) { // ①
String s = name.toLowerCase(); // ②
int len = s.length(); // ③
if (len < 10) { // ④
int p = 10 - len; // ⑤
for (int i=0; i<10; i++) { // ⑥
System.out.println(); // ⑦
} // ⑧
} // ⑨
} // ⑩
}
-
方法参数name是局部变量,它的作用域是整个方法,即①~⑩;
-
变量s的作用域是定义处到方法结束,即②~⑩;
-
变量len的作用域是定义处到方法结束,即③~⑩;
-
变量p的作用域是定义处到if块结束,即⑤~⑨;
-
变量i的作用域是for循环,即⑥~⑧。
final
final
关键字与作用域的关键字并不冲突,它只是在作用域关键字的基础上再添加一道枷锁以实现一些特定的作用,包括:
- 用
final
修饰class
可以阻止被继承:
package abc;
// 无法被继承:
public final class Hello {
private int n = 0;
protected void hi(int t) {
long i = t;
}
}
- 用
final
修饰method
可以阻止被子类覆写:
package abc;
public class Hello {
// 无法被覆写:
protected final void hi() {
}
}
- 用
final
修饰field
可以阻止被重新赋值:
package abc;
public class Hello {
private final int n = 0;
protected void hi() {
this.n = 1; // error!
}
}
- 用
final
修饰局部变量可以阻止被重新赋值:
package abc;
public class Hello {
protected void hi(final int t) {
t = 1; // error!
}
}
总的来说,用 final
修饰过后,就不允许被修改和被继承,类似于被固化了。
总结
- 对象的被访问权限主要由
public
,private
,protected
,package
所规定,但又不仅仅由这些所规定。 - 一个文件里只允许由一个
public
类,且文件名要与public
类名一致。 final
是一个固化器,不允许继承、覆写、修改。- 局部变量的声明周期由创建点和所在大括号的右括号(所在块的终止处)决定。