Java学习 作用域/内部类/classpath和jar

JAVA学习

作用域

public

定义为public的class interface可以被其他任何类访问:

package abc;

public class Hello {
    public void hi() {
    }
}

上边的hello是public 因此可以被其他包的类访问:

package xyz;

class Main {
    void foo() {
        // Main可以访问Hello
        Hello h = new Hello();
    }
}

定义为public的field method 可以被其他类访问,前提是首先有访问class的权限

private

定义为private的field method无法被其他类访问:

package abc;

public class Hello {
    // 不能被其他类调用:
    private void hi() {
    }

    public void hello() {
        this.hi();
    }
}

private访问权限被限定在class内部,而且与方法声明顺序无关。推荐把private方法放到后面,因为public方法定义了类对外提供的功能,阅读代码的时候,应该先关注public方法:

package abc;

public class Hello {
    public void hello() {
        this.hi();
    }

    private void hi() {
    }
}

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("private hello!");
    }

    // 静态内部类:
    static class Inner {
        public void hi() {
            Main.hello();
        }
    }
}

定义在class内部的class称为嵌套类

protected

protected作用于继承关系,定义为protected的字段和方法可以被子类访问,以及子类的子类

package abc;

public class Hello {
    // protected方法:
    protected void hi() {
    }
}

上面的protected方法可以被继承的类访问

package xyz;

class Main extends Hello {
    void foo() {
        // 可以访问protected方法:
        hi();
    }
}
package

包的作用域是指一个类允许访问同一个package的没有public private修饰的class,以及没有public protected private修饰的字段和方法

package abc;
// package权限的类:
class Hello {
    // package权限的方法:
    void hi() {
    }
}

只要在同一个包中,就可以访问package权限的class field和method

package abc;

class Main {
    void foo() {
        // 可以访问package权限的类:
        Hello h = new Hello();
        // 可以调用package权限的方法:
        h.hi();
    }
}
局部变量

方法内部定义的变量称为局部变量,局部变量作用域从变量声明处到对应的块结束,方法参数也是局部变量。

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(); // ⑦
            } // ⑧
        } // ⑨
    } // ⑩
}

我们观察上面的hi()方法代码:

方法参数name是局部变量,它的作用域是整个方法,即①~⑩;

变量s的作用域是定义处到方法结束,即②~⑩;

变量len的作用域是定义处到方法结束,即③~⑩;

变量p的作用域是定义处到if块结束,即⑤~⑨;

变量i的作用域是for循环,即⑥~⑧。

使用局部变量时,应该尽可能把局部变量的作用域缩小,尽可能延后声明局部变量。

final

final不是访问权限,final有很多作用

  • 用final修饰的class可以阻止被继承;
  • 用final修饰的method可以阻止被子类覆写
  • 用final修饰的field可以阻止被重新赋值
package abc;

public class Hello {
    private final int n = 0;
    protected void hi() {
        this.n = 1; // error!
    }
}
  • 用final修饰的局部变量可以阻止被重新赋值

TIPS
如果不确定是否需要public,就不声明为public,即尽可能少地暴露对外的字段和方法。

把方法定义为package权限有助于测试,因为测试类和被测试类只要位于同一个package,测试代码就可以访问被测试类的package权限方法。

一个.java文件只能包含一个public类,但可以包含多个非public类。如果有public类,文件名必须和public类的名字相同。

内部类

定义在类内部的类,称之为内部类(Nested Class)

Inner Class

如果一个类定义在另一个类的内部,这个类就是Inner Class:

class Outer {
    class Inner {
        // 定义了一个Inner Class
    }
}

上述的Outer是一个普通类,而Inner 是一个Inner class 与普通类有个最大的不同,Inner Class的实例不能单独存在,必须依附于一个Outer Class的实例。

public class Main {
    public static void main(String[] args) {
        Outer outer = new Outer("Nested"); // 实例化一个Outer
        Outer.Inner inner = outer.new Inner(); // 实例化一个Inner
        inner.hello();
    }
}

class Outer {
    private String name;

    Outer(String name) {
        this.name = name;
    }

    class Inner {
        void hello() {
            System.out.println("Hello, " + Outer.this.name);
        }
    }
}

要实例化一个Inner,必须首先创建一个Outer的实例,然后调用Outer实例的new来创建Inner实例:

Outer.Inner inner = outer.new Inner();

Inner Class 除了有一个this指向他自己,还隐含地持有一个Outer Class实例,可以用Outer。this访问这个实例,所以,实例化一个Inner Class不能脱离Outer实例。

Inner Class和普通地Class相比,除了能引用Outer实例外,还有一个额外地特权,可以修改Outer class的private字段。

观察Java编译器编译后的.class文件可以发现,Outer类被编译为Outer.class,而Inner类被编译为Outer$Inner.class。

Anonymous Class

另外一种定义Inner Class的方法,不需要再Outer Class中明确定义这个class,而是在方法内部,通过匿名类(Anonymous Class)来定义,

public class Main {
    public static void main(String[] args) {
        Outer outer = new Outer("Nested");
        outer.asyncHello();
    }
}

class Outer {
    private String name;

    Outer(String name) {
        this.name = name;
    }

    void asyncHello() {
        Runnable r = new Runnable() {
            @Override
            public void run() {
                System.out.println("Hello, " + Outer.this.name);
            }
        };
        new Thread(r).start();
    }
}

在方法内部实例化了一个Runnable ,Runnable本身是接口,接口不能实例化,这里实际上是定义了一个实现了Runnable接口的匿名类,并且通过new实例化该匿名类,然后转型为Runnable,再定义匿名类的时候就必须实例化它 定义匿名类的写法如下:

Runnable r = new Runnable() {
    // 实现必要的抽象方法...
};

观察Java编译器编译后的.class文件可以发现,Outer类被编译为Outer.class,而匿名类被编译为Outer$1.class。如果有多个匿名类,Java编译器会将每个匿名类依次命名为Outer$1、Outer$2、Outer$3……

除接口外,匿名类也完全可以继承自普通类,关系以下代码:

import java.util.HashMap;

public class Main {
    public static void main(String[] args) {
        HashMap<String, String> map1 = new HashMap<>();
        HashMap<String, String> map2 = new HashMap<>() {}; // 匿名类!
        HashMap<String, String> map3 = new HashMap<>() {
            {
                put("A", "1");
                put("B", "2");
            }
        };
        System.out.println(map3.get("A"));
    }
}

map1是一个普通的HashMap实例,但map2是一个匿名类实例,只是该匿名类继承自HashMap。map3也是一个继承自HashMap的匿名类实例,并且添加了static代码块来初始化数据。观察编译输出可发现Main$1.class和Main$2.class两个匿名类文件。

Static Nested Class

最后一种内部类和Inner Class类似,但是使用stati修饰 称为静态内部类(static nested class)

public class Main {
    public static void main(String[] args) {
        Outer.StaticNested sn = new Outer.StaticNested();
        sn.hello();
    }
}

class Outer {
    private static String NAME = "OUTER";

    private String name;

    Outer(String name) {
        this.name = name;
    }

    static class StaticNested {
        void hello() {
            System.out.println("Hello, " + Outer.NAME);
        }
    }
}

用static修饰的内部类和Inner Class有很大的不同,它不再依附于Outer的实例,而是一个完全独立的类,因此无法引用Outer.this,但它可以访问Outer的private静态字段和静态方法。如果把StaticNested移到Outer之外,就失去了访问private的权限。

class path和jar

class path

class是JVM用到的一个环境变量,它用来指示JVM如何搜索class
因为Java是编译型语言,源码文件是.java,而编译后的.class文件才是真正可以被JVM执行的字节码。因此,JVM需要知道,如果要加载一个abc.xyz.Hello的类,应该去哪搜索对应的Hello.class文件。

jar

jar包相当于目录,可以包含很多.class文件,方便下载和使用;

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值