剖析Java中的访问/非访问控制符以及神奇的main函数

你好,我是goldsun

你有想法和我一起进步吗?

前言

我们都知道Java是一门完全面向对象的编程语言,因此它也完全具备继承、封装、多态的三大特性。

特性中的继承

其中继承则是面向对象的程序设计中最为重要的特征之一。由继承得到的类为子类,被继承的类称为父类或者超类。一个父类可以同时拥有多个子类,但是Java不支持多重继承,即一个类只能有一个直接父类。父类是对所有子类的公共域和公共方法的集合,而每一个子类则是父类的特殊化,是对公共域和方法在功能、内涵方面的扩展和延伸。
既然子类继承父类的状态和行为,那么子类便可以访问或修改或重载等父类的状态或行为。而有时候我们是不想子类访问或修改父类行为的,亦或者有其他需求,在这种情形下,为了更好的控制类及其域、 方法等的存取权限,更高地实现信息的封装与隐藏,需要用到访问/非访问控制修饰符。

访问控制符

访问控制符总共有三个,如果算上默认则等于有四个。这四种访问控制符包含了Java中对类成员在四种范围中的访问权限的控制,这四种范围分别是:同一个类中,同一个包中,不同包中的子类,不同包中的非子类。
如下是相应访问权限表:

/ 同一个类中 同一个包中 不同包中的子类 不同包中的非子类
private 可访问
默认 可访问 可访问
protected 可访问 可访问 可访问
public 可访问 可访问 可访问 可访问

成员访问控制符

private

私有对象声明示例如下:

private privateVar;
private privateMethod([paramlist]){
	......	
}
  • 在一个类中限定为private的成员只能被这个类本身访问,即代表它是这个类私有的,不能被其它类的成员进行访问,即使是这个类的子类,也不能对private成员进行访问,而在继承此类时,private的域或方法不能被子类所继承。
  • 而在一个类的代码中,一个类的不同对象可以访问对方的private成员变量或调用对方的private方法,这是因为访问控制保护是在这个类的级别上,而不是对象的级别上。
默认
  • 默认访问控制就是在成员前没有访问控制符修饰。它的访问权限是整个包。即在整个包中的所有类中都可以访问它。
protected
  • 类中有protected修饰符修饰的成员可以被这个类本身、它的子类(包括同一个包或者不同包中的子类)及同一个包中的所有其它的类访问。声明示例如下:
protected protectedVar;
protected protectedMethod([pamramlist]){
	......
}
  • 而此访问控制符可以和私有访问控制符组合成:private protected,它的作用是限定能被此类及其子类可以访问,而包中的其它非子类的类不能访问。
public

对于public修饰的对象就比较好理解了,即此修饰符修饰的变量是可以允许所有地方的所有类对其进行访问的,它不能和其它两个访问控制修饰符进行结合。
声明示例如下:

public publicVar;
public PublicMethod([paramlist]){
	......
}

类的访问控制符

在定义类的时候,也可以使用访问控制符。类的访问控制符为public,即我们所知道的公共类,如果用此访问控制符进行修饰,则这个类可以被其它的类进行访问,若为默认,则此类只能被同包中的类进行访问,需要注意的是,一个Java程序中只能有一个公共类。

非访问控制符

在Java中常用的非访问控制符一般有两个:static,final,其它还有一些非访问控制符如:abstract,volatile,native,synchronized,相对于后四者而言,前两者用的更多,下文对其进行说明。

  • 控制符特征:
非访问控制符 基本含义 修饰类 修饰成员 修饰局部变量
static 静态的、非实例的、类的 只能修饰嵌套类 可以修饰
final 最终的、不可改变的 可以修饰 可以修饰 可以修饰
static
  • 用static修饰符修饰的域或方法成为类域或者类方法,也可以成为静态量或者静态方法,而没有此修饰符的域或方法称为实例变量和实例方法。
静态域
  • 静态域我们可以理解为类的一个常量,注意它是属于类的一个常量而不单单属于一个实例,即同一个类的任何一个对象实例去修改它,之后所有实例取到的都是修改后的数值,即此变量不保存在某个对象实例的内存区间中,而是保存在类的内存区域的公共存储单元中。
  • 其实我们可以用一句话来概括static修饰的变量,即它可以当作全局变量来使用。

用一个示例来更好的理解它:

public class practice {
    public static void main(String args[]) {
        Person goldsun1 = new Person();
        Person goldsun2 = new Person();
        goldsun1.age = 19;
        goldsun2.age = 21;
        goldsun1.trueage = 20;
        System.out.println(goldsun1.age);
        System.out.println(goldsun2.age);
        System.out.println(goldsun1.trueage);
        System.out.println(goldsun2.trueage);
        System.out.println(Person.trueage);
    }
}
class Person{
    static long trueage;
    int age;
}
/*输出
19
21
20
20
20

需要注意的是,虽然可以使用实例变量对static成员进行访问,但这并不意味着这个成员就是这个实例的。编译器实际上将goldsun1.trueage翻译成了Person.trueage。

静态方法

有了静态域的对比,静态方法就比较好理解了,它也是属于整个类的方法,而不是某个实例的方法。

  • 因为所有成员(包括通过实例和类名)都可以对静态方法进行访问修改等,因此静态方法中不允许存取实例变量。静态方法只允许处理属于整个类的成员,即只能处理静态量或者调用静态方法。
  • 在调用类方法时也可以通过实例名或者类名。

示例:

public class practice {
    public static void main(String args[]) {
        Person goldsun1 = new Person();
        Person goldsun2 = new Person();
        goldsun1.setParams(20,19);
        goldsun1.setTrueage(100);
        goldsun2.setParams(15,16);
        goldsun2.setTrueage(50);
        System.out.println(goldsun1.age);
        System.out.println(goldsun1.trueage);
        System.out.println(goldsun2.age);
        System.out.println(goldsun2.trueage);
    }
}
class Person{
    static long trueage;
    int age;
    static void setTrueage(long i){
        trueage = i;
        //age = i; 不能在静态方法中访问实例变量
    }
    void setParams(long i,int j){
        trueage = i;//实例方法中既可以访问实例变量也可以访问静态变量
        age = j;
    }
}
/*输出
19
50
16
50
final
final类
  • 一个类可以被final修饰符修饰,如果使用final修饰一个类,说明这个类不能被继承,即此类不可能有子类。被定义为final的类一般是一些具有固定作用或者来完成一些标准功能的类。如’Double、String、Math’等,将一个类定义为final类型可以保证这个类的功能属性等被固定下来,从而保证在引用此类时实现的功能不会出现错误。
final方法
  • final修饰的方法是不能被子类所覆盖的方法。即如果某个类的方法被final修饰,则此类的子类便不能再重新定义与此方法同名的自己的方法,这样可以保证此方法只能实现某一对应操作,防止子类对父类的一些关键方法进行错误的重定义,保证程序安全和准确。
  • 所有已被private修饰符限定的方法,以及所有包含再final类中的方法,都被默认为是final的。因为这些方法均不能被子类继承,所有都不可能被重载,所以都是最终的方法。
final字段和局部变量

对于final修饰的字段和局部变量,它们的值一旦被给定,是不能更改的,它们都是只读量,如果在定义的时候没有初始化赋值,则之后能且只能被赋值一次,不能被赋值多次。

而我们知道static修饰静态量,则static可以与final结合起来为static final,它的意思就是常量,如圆周率 π \pi ,我们在使用过程中不需要对其修改,并且可以全局使用。

  • 在定义static final时,若不给定初始值,会按默认值进行初始化:数值为0,布尔型为false,引用型为null。
  • 在定义final字段时,若没有static,必须只能赋值一次,不能缺省。这种字段的赋值方式有两种:一种是在定义变量的时候就赋予初始值,另一种是在构造函数中进行赋值。
  • 在final局部变量(某个方法的变量)中,也必须且只能赋值一次。
public class practice {
    public static void main(String args[]) {
        Person goldsun1 = new Person(15);
        System.out.println(goldsun1.age);
        System.out.println(goldsun1.pi);
        //goldsun1.age = 20;非法赋值
        final int tureage ;
        tureage = 20;
        //tureage = 30; 非法赋值
        System.out.println(tureage);
    }
}
class Person{
    static final float pi = 3.1415f;
    final int age;
    Person(int i){
        age = i;
    }
}
/*输出
15
3.1415
20

main()方法

  • 对于一些新学习Java的同学比如我,可能会对Java的一些结构包括执行过程有点小懵,比如为什么Java程序的文件名一定要是和公共类的名称一样,而我们经常写程序的时候总是把main()方法放在程序的唯一一个公共类中,这又是为什么,难道把main()方法放在别的类中不行吗?其实我们都知道main()方法是程序执行的入口,但又还是有些不太理解。

实际上,程序中的main()方法不是必须要放在唯一的一个公共类中的,它可以放在其它类当中,但正常情况下这样做会报错,这是为什么呢?是因为程序在执行的过程中第一步是调用:

文件名.main()

正常情况下这里的文件名就是我们程序中的公共类名,而通过这种方法进行调用即通过类名进行调用方法,因为并没有创建实例进行调用,因此这个方法也就必须是类方法也就是静态方法,这也就解释了为什么main()方法的前面还总是要有static
也就是说,程序执行的时候只会执行公共类中的main()方法,就像在Python中定义了一个main()函数一样,在执行时程序只需要执行下面的main()一样

def xxx():
	...
def xx():
	...
def main():
	...
if '__name__' == '__main__':
	main()

而为什么说也可以不把main()方法放在公共类中呢,因为上文说了,在程序运行过程中是默认执行公共类中的main()方法,那只要你把配置改一下,让程序去你放main()方法的那个类去找main不就行了嘛

发布了23 篇原创文章 · 获赞 72 · 访问量 8278

猜你喜欢

转载自blog.csdn.net/weixin_45634606/article/details/105446707