java访问控制修饰符、static、static final常量、枚举

通过前几章的学习,我们了解到了java面向对象的几大特征,继承、抽象、接口、多态等知识,而抽象、封装、继承、多态就是我们java的四大基本特性,其中封装、继承、多态是我们java的三大基本特性,那么封装是什么呢?封装就是我们接下来要说的访问控制修饰符来完成的,简单来说封装就是把我们的代码隐藏起来不让外部访问到,外部想要访问的话要通过我们给定的规则来访问,否则访问不了,封装后面会详细讲到,我们现在先了解一下。

一、访问控制修饰符

在java中我们有四大访问控制修饰符,访问控制修饰符从大到小为:public、protected、default、private。我们java的控制修饰符是用来保护数据的安全(隐藏数据---->把我们的属性用private修饰、暴露行为---->把我们的行为用public来修饰),以此来实现我们的封装。

public:   公开的、任何类

                如果一个成员方法或者成员变量名前面使用了 public 访问控制符,那么这个成员被所有的  类访问,不管访问类与被访问类是否在同一个包中。

protected:受保护的、本类、派生类(子类)、同包类

                 如果一个成员方法或者成员变量前面使用了 protected 访问控制符,那么这个成员即可  以被同一个包中其他类访问,有可以被不同包的子类访问。

default:(默认权限)本类、同包类 

                如果一个成员方法或者成员变量名前面没有使用任何访问控制符,就称这个成员所拥有的是 默认的(default)访问控制符。默认的访问控制成员可以被这个包中的其他类访问。如果一个子类与其父类位于不同包中,子类也不能访问父类中的默认访问控制成员。

private: 私有的、本类

                如果一个成员方法或成员变量的前面使用了 private 访问控制符,那么这个成员只能在这个类的那内部使用。

我们需要知道的是类只能用public或者默认权限default来修饰,而类中的成员的访问权限可以有以上四种。

package ooday05;
public class Aoo {
    public int a;    //任何类
    protected int b; //本类、子类、同包类
    int c;           //本类、同包类
    private int d;   //本类

    void show(){
        a = 1;
        b = 2;
        c = 3;
        d = 4;
    }
}

class Boo{ //-------------------演示private
    void show(){
        Aoo o = new Aoo();
        o.a = 1;
        o.b = 2;
        o.c = 3;
        //o.d = 4; //编译错误
    }
}

package ooday05_vis;
import ooday05.Aoo;
public class Coo { //-------------------演示同包的
    void show(){
        Aoo o = new Aoo();
        o.a = 1;
        //o.b = 2; //编译错误
        //o.c = 3; //编译错误
        //o.d = 4; //编译错误
    }
}

class Doo extends Aoo{ //跨包继承-------------演示protected
    void show(){
        a = 1;
        b = 2; 
        //c = 3; //编译错误
        //d = 4; //编译错误
    }
}

从上面代码我们可以看出即使Boo类和Aoo类在同一包下,我们用private修饰的成员,在Boo下也访问不到,被我们隐藏起来了,而不同包的情况下,只要没有继承关系时,我们只能访问到public修饰的成员,其他的都访问不到。而不同包,但是有父子类的关系的情况下,我们可以访问到public和protected修饰的成员。

二、static关键字

在java中,static表示静态的,是一种修饰符,他可以修饰我们的变量、方法、代码块 、以及内部类,被static修饰的变量叫静态变量,也称类变量、被static修饰的方法称为静态方法,也可以叫类方法,被static修饰的常量称为静态常量,同理被static修饰的代码块、内部类我们称为静态代码块、静态内部类。

其中,静态变量,静态方法,静态常量我们统称为类的静态成员,归整个类所有,不属于某个单一的对象也就是说,静态成员不属于某个对象单独拥有,而是被类的所有实例对象共享。所以,我们的静态成员不需要通过对象来进行访问,而是直接通过类来访问(类名.静态成员)。只要这个类被加载,java会自动把其加入到方法区中去,我们虚拟机就可以通过类名来访问它。

我们需要注意的是:

1、static关键词修饰的成员变量和方法都属于类,不属于某个对象;

2、普通变量和方法属于某个对象,每个对象都有自己的变量和方法,彼此之间是隔离的;

3、静态方法不能调用非静态的变量和非静态的方法,否则编译时就会报错。

2.1 静态变量 

在Java类中,由static修饰的属性(成员变量)叫做静态变量,也叫类变量。而被static修饰的常量就叫做静态常量,静态变量和静态常量统称为静态成员。注意,局部变量不能被声明为静态变量,对于静态变量和静态常量来说,无论一个类实例化出来多少个对象,它的静态成员都只有一份拷贝,可以被所有的对象共享,可以说,实例成员在每个实例对象中都有自己的一个独立的私有“空间”,但是静态成员只有一个共享的公共“空间”,所有实例对象都会共享该静态成员。

静态变量:由static修饰,属于类,存储在方法区中,只有一份,常常通过类名点来访问,何时用:对象所共享的数据。

public class StaticVar {
    int a; //实例变量
    static int b; //静态变量
    StaticVar(){
        a++;
        b++;
    }
    void show(){
        System.out.println("a="+a+",b="+b);
    }
}

public class StaticDemo {
    public static void main(String[] args) {
        StaticVar o1 = new StaticVar();   
        o1.show();                       //a=1,b=1
        StaticVar o2 = new StaticVar();
        o2.show();                       //a=1,b=2
        StaticVar o3 = new StaticVar();
        o3.show();                       //a=1,b=3
        System.out.println(StaticVar.b); //常常通过类名点来访问
    }
}

静态变量内存图: 

静态变量与实例变量的区别:

静态变量:

1、JVM虚拟机只会为静态变量分配一次内存,在加载类的过程中完成对静态变量的内存分配;

2、我们可以在类的任意方法中直接访问任意静态变量;

3、我们可以在其他类中通过"类名.静态变量"的形式,来访问该类中的静态变量。

实例变量:

1、 每创建一个Java实例对象,JVM虚拟机就会为该实例变量分配一次内存;

2、我们可以在类的非静态方法中直接访问实例变量;

3、在类的静态方法中,需要通过"对象.实例变量"的形式进行访问。

2.2、静态块

1、由 static 修饰的代码块
2、属于类,在类被加载期间自动执行,一个类只被加载一次,所以静态块也只执行一次,静态块优先于构造块执行,并且不管有多少个实例化对象,静态块只执行一次,在大部分情况下静态块 往往会作为一些公共属性的初始化操作。
3、何时用:初始化 / 加载静态资源 / 静态变量
注意:当静态块与静态变量同时出现时,执行顺序是按照默认顺序执行(从上到下,从左到右),即代码块中有静态变量m,那么m的最终值为m最后出现的位置,最后所赋的值。
public class StaticBlock {
    static{
        System.out.println("静态块");
    }
    StaticBlock(){
        System.out.println("构造方法");
    }
}
    public class StaticDemo {
    public static void main(String[] args) {
    StaticBlock o4 = new StaticBlock(); //加载类时自动执行静态块
    StaticBlock o5 = new StaticBlock();
    StaticBlock o6 = new StaticBlock();
    }
}

结果如下:可以看出我们静态块就执行了一次,不会重复的执行!

2.3、静态方法 

1、由 static 修饰的方法
2、属于类,存储在方法区中,只有一份 ,常常通过类名点来访问
3、静态方法中没有隐式 this 传递,所以静态方法中不能直接访问实例成员 ( 实例变量 / 实例方
)
何时用:方法的操作与对象无关 ( 不需要访问对象的属性 / 行为 )

 

public class StaticMethod {
    int a; //实例变量(对象来访问)--------------属于对象的
    static int b; //静态变量(类名来访问)-------属于类的

    //静态方法何时用:方法的操作与对象无关(不需要访问对象的属性/行为)

    //在say()中需要访问对象的属性a,所以认为say的操作与对象有关,不适合设计为静态方法
    void say(){
        System.out.println(a);
    }
    //在plus()中不需要访问对象的属性/行为,所以认为plus的操作与对象无关,可以设计为静态方法
    static int plus(int num1,int num2){
        int num = num1+num2;
        return num;
    }

    void show(){ //有隐式this
        System.out.println(this.a);
        System.out.println(StaticMethod.b);
    }
    static void test(){ //没有隐式this
        //静态方法中没有隐式this传递
        //没有this就意味着没有对象
        //而实例变量a必须通过对象来访问
        //所以如下语句发生编译错误
        //System.out.println(a); //编译错误,静态方法中不能直接访问实例成员
        System.out.println(StaticMethod.b);
    }
}

public class StaticDemo {
    public static void main(String[] args) {
        StaticMethod.test(); //常常通过类名点来访问
    }
}

2.4、static final 常量 

常量的应用率还是挺高的,怎么用如下:

1、必须声明同时初始化
2、常常通过类名点来访问,不能被改变 ,建议:常量名所有字母都大写,多个单词之间用_ 分隔
3、编译器在编译时,会将常量直接替换为具体的数,效率高 ,何时用:在程序运行过程中数据永远不变,并且经常使用。
public class StaticFinalDemo {
    public static void main(String[] args) {
        System.out.println(Loo.PI); //常常通过类名点来访问
        //Loo.PI = 3.1415926; //编译错误,常量不能被改变

        //1)加载Loo.class到方法区中
        //2)静态变量num一并存储到方法区中
        //3)到方法区中获取num的值并输出
        System.out.println(Loo.num);

        //编译器在编译时会将常量直接替换为具体的数,效率高
        //相当于System.out.println(5);
        System.out.println(Loo.COUNT);
    }
}

class Loo{
    public static int num = 5; //静态变量
    public static final int COUNT = 5; //常量(静态常量)

    public static final double PI = 3.14159;
    //public static final int NUM; //编译错误,常量必须声明同时初始化
}

三、枚举

 枚举是一种引用数据类型 ,特点:枚举类型的对象数目是固定的,常常用于定义一组常量,所有枚举都继承自Enum类,其中提供了一组方法供我们使用 ,枚举的构造方法都是私有的。

我们可以理解为枚举为我们事先定义好的一系列自定义的数据。

//简单版:
package ooday05;
/**
 * 季节枚举
 */
public enum Seasons {
    SPRING,SUMMER,AUTUMN,WINTER //表示Seasons的固定的4个对象,都是常量
}

package ooday05;
/**
 * 枚举的测试类
 */
public class EnumTest {
    public static void main(String[] args) {
        Seasons[] seasons = Seasons.values(); //获取所有枚举对象
        for(int i=0;i<seasons.length;i++){
            System.out.println(seasons[i]);
        }
        /*
        Seasons s = Seasons.WINTER; //获取WINTER对象
        switch(s){
            case SPRING:
                System.out.println("春天到了...");
                break;
            case SUMMER:
                System.out.println("夏天到了...");
                break;
            case AUTUMN:
                System.out.println("秋天到了...");
                break;
            case WINTER:
                System.out.println("冬天到了...");
        }
         */
    }
}

结果如下:(最后一个结果为注释部分)

枚举复杂一点的案例:

 //复杂版:
 package ooday05_vis;
 /**
  * 季节枚举
  */
 public enum Seasons {
     SPRING("春天","暖和"),
     SUMMER("夏天","热"),
     AUTUMN("秋天","凉爽"),
     WINTER("冬天","冷"); //创建季节对象
     private String seasonName;
     private String seasonDesc;
     Seasons(String seasonName, String seasonDesc) {
         this.seasonName = seasonName;
         this.seasonDesc = seasonDesc;
     }
 
     public String getSeasonName() {
         return seasonName;
     }
     public void setSeasonName(String seasonName) {
         this.seasonName = seasonName;
     }
     public String getSeasonDesc() {
         return seasonDesc;
     }
     public void setSeasonDesc(String seasonDesc) {
         this.seasonDesc = seasonDesc;
     }
 }
 
 package ooday05_vis;
 
 /**
  * 枚举的演示
  */
 public class EnumTest {
     public static void main(String[] args) {
         Seasons s = Seasons.WINTER;
         System.out.println(s.getSeasonName()+","+s.getSeasonDesc());
 
         Seasons[] seasons = Seasons.values();
         for(int i=0;i<seasons.length;i++){
             System.out.println(seasons[i]);
             System.out.println(seasons[i].getSeasonName());
             System.out.println(seasons[i].getSeasonDesc());
         }
     }
 }

四、补充 

1.数据 ( 成员变量 ) 私有化 (private) 、行为 ( 方法 ) 大部分公开化 (public)----开发好习惯
2. 接口中的成员的默认权限为 public ,重写方法时派生类方法的访问权限必须大于或等于超类方法
3. getter/setter :行业标准

 

package ooday05;
/*
  标准JavaBean的规范:
  1.成员变量私有,同时提供对应的公开的getter/setter
  2.包含公开的无参构造方法

  设计getter/setter的原因:
  1.很多框架的配置操作都是基于getter/setter的,没有它就获取不到数据,
    可以将设计getter/setter理解为一种行业标准
  2.可以更好的保证数据的合法性(因为方法中可以做条件控制)
  3.getter/setter可以选择性存在(只有getter(只读),或者只有setter(只写))
 */
public class Point {
    private int x;
    private int y;
    public Point(){
    }

    public int getX(){ //getter获取
        return x;
    }
    public void setX(int x){ //setter设置
        this.x = x;
    }
    public int getY(){
        return y;
    }
    public void setY(int y){
        this.y = y;
    }
}

package ooday05;
//getter/setter的演示
public class GetterSetterDemo {
    public static void main(String[] args) {
        Point p = new Point();
        p.setX(100);
        p.setY(200);
        System.out.println(p.getX()+","+p.getY());
    }
}

4.成员变量分两种:
         实例变量:没有static 修饰,属于对象的,存储在堆中,有几个对象就有几份,通过引用 / 对象
打点来访问
        静态变量:有static 修饰,属于类的,存储在方法区中,只有一份,通过类名打点来访问

 

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值