Java中的访问权限修饰符
Java语言有四个权限访问修饰符,权限从大到小依次为:
1)public :公共权限,可以被任意类访问,不同包不同类依然可以访问,
可修饰:类、成员变量、方法,内部类
2)protected:受保护的权限,可以被同包类访问,如果不是同包类,必须是该类的子类才可以访问
可修饰:成员变量、方法、内部类
3)default:默认的(无),同包权限,只能被同包的类访问
可修饰:类、成员变量、方法,内部类
4)private:私有权限,只能在本类中访问,同包其他类也不能访问
可修饰:成员变量、方法、内部类
补充说明:在Java中,default和friendly是同一个访问级别的两种不同称呼。当一个成员没有被声明为private、protected或public时,它就具有默认访问级别,也被称为friendly访问级别。默认访问级别仅适用于同一个包中的类。这意味着,只有在同一个包内的其他类可以访问默认访问级别的成员,而在其他包中的类无法访问。总结来说,default和friendly修饰符没有区别,它们都表示同一个访问级别,仅适用于同一个包中的类
类
一个类只能继承 extends 一个类,但可以实现 implements 多个接口。
抽象类 abstract
抽象类不能实例化
可以没有抽象方法,但抽象方法只能存在于抽象类。
抽象方法不能用 private 修饰(抽象方法需要子类覆盖),不能用 static 修饰(如果修饰了,那可以通过类名调用,而抽象方法没有主体)。
抽象类不能用 final 修饰, 因为 final 修饰的类是无法继承的。
一个类继承抽象类,如果不完全覆盖抽象类中的抽象方法,那么这个类必须是抽象类。
抽象类可以有构造方法,可以实体方法实现具体的代码逻辑。
final
final 的使用方式
final 修饰的类叫最终类,该类不能被继承。
final 修饰的方法不能被重写。
final 修饰的变量叫常量,常量必须初始化,初始化之后值就不能被修改。
static
如果一个类要被声明为static的,只有一种情况,就是静态内部类。如果在外部类声明为static,程序会编译都不会过。
修饰变量
表示这个变量的生命周期是类的存在周期。可以通过类名去访问,也可以通过对象去访问。
修饰方法
static修饰的方法只能访问 static变量,不能访问非 static 变量。
内部类
可以解决单继承问题,实现多继承。
成员内部类
public class Outer { public class Inter { } }
特点:
可以被权限修饰符(public, private等)所修饰
可以访问外部类的所有成员,包括 private 成员、static成员
默认包含了一个指向外部类对象的引用
如同使用 this 一样,当成员或方法名发生覆盖时,可以使用外部类的名字加 .this 制定访问外部成员。如: Outer.this.name
不可用定义 static 成员
内部类创建语法
Outer outer = new Outer(); Outer.Inner inner = outer.new Inner();
局部内部类
定义在方法或者作用域中类,它和成员内部类的区别仅在于访问权限的不同。
public class Outer { public void test() { class Inner { } } }
特点:
不能有权限访问修饰符
不能被定义为 static
不能定义 static 成员
默认包含了外部类对象的引用
可以使用 Outer.this 语法制定访问外部类成员
想要使用方法或域中的变量,该变量必须是 final 的。(JDK1.8 之后,可以没有final 修饰,但需要具有 effectively final效果)
匿名内部类
匿名内部类与继承合并在一起的没有名字的内部类。
/** * 外部内、内部类 */ public class Outer { public static IAnimal getInnerInstance(String speak){ return new IAnimal(){ @Override public void speak(){ System.out.println(speak); }}; //注意上一行的分号必须有 } public static void main(String[] args){ //调用的speak()是重写后的speak方法。 Outer.getInnerInstance("小狗汪汪汪!").speak(); } }
特点:
使用单独的块表示初始化块{}
想要使用方法或域中的变量,该变量必须是final修饰的,JDK1.8之后effectively final 也可以
默认包含外部类对象的引用
表示继承所依赖的类。如上面的,表示继承 IAnimal 类或者 实现 IAnimal 接口
嵌套类
嵌套类是用 static修饰的成员内部类。
public class Outer{ public static class Inner{ } }
特点:
嵌套类是四种类中唯一一个不包含对外部类对象的引用的内部类
嵌套类可以定义 static 成员
嵌套类能访问外部类任何静态数据成员与方法。
构造函数可以看做静态方法,因此可以访问。
泛型
泛型类
public class Generic<T> { private T a; public Generic(); public void set(T a) { this.a = a; } public T get() { return a; } public static void main(String[] args) { Generic<Integer> g = new Generic<>(); g.set(1); } }
泛型接口
public interface Generator<T { pubilc T method(); }
实现泛型接口,不制定类型:
class GeneratorImpl<T> implements Generator<T> { public T method() { return null; } }
实现泛型接口,指定类型:
class GeneratorImpl<T> implements Generator<String> { public String method() { return "hello"; } }
泛型方法
如果某个方法是静态的,它便没有访问类的泛型类型参数的权限(需要类实例化才知道具体的参数)。因此如果要用到泛型能力,它就必须是泛型方法。
package com.testclass.generics; public class GenericMethods { public <T> void f(T x) { System.out.println(x.getClass().getName()); } public static void main(String[] args) { GenericMethods g = new GenericMethods(); g.f(""); g.f(1); g.f(g); } }
类型“擦除”
List<String> 和 List<Integer> 在运行时实际上是相同的类型。两者的类型都被“擦除”为它们原始的类型(raw type):List。
类型擦除存在的主要理由就是充当从非泛型代码过渡到泛型化代码的中间过程,以及在不破坏先有库的情况下,将泛型融入 Java 语言中。
泛型边界
package com.testclass.generics; interface HasColor { java.awt.Color getColor(); } class WithColor<T extends HasColor> { T item; WithColor(T item) { this.item = item; } T getItem() { return item; } // 可以调用边界中的方法 java.awt.Color color() { return item.getColor(); } } class Coord { public int x, y, z; } // 这样会失败。类(Coord)必须在前面,然后才是接口 (HasColor) // class WithColorCoord<T extends HasColor & Coord> // 多重边界 class WithColorCoord<T extends Coord & HasColor> { T item; WithColorCoord(T item) { this.item = item; } T getItem() { return item; } java.awt.Color color() { return item.getColor(); } int getX() { return item.x; } int getY() { return item.y; } int getZ() { return item.z; } } interface Weight { int weight(); } // 和使用继承一样,只能继承一个具体类,而可以实现多个接口 class Solid<T extends Coord & HasColor & Weight> { T item; Solid(T item) { this.item = item; } T getItem() { return item; } java.awt.Color color() { return item.getColor(); } int getX() { return item.x; } int getY() { return item.y; } int getZ() { return item.z; } int weight() { return item.weight(); } } class Bounded extends Coord implements HasColor, Weight { @Override public java.awt.Color getColor() { return null; } @Override public int weight() { return 0; } } public class BasicBounds { public static void main(String[] args) { Solid<Bounded> solid = new Solid<>(new Bounded()); solid.color(); solid.getY(); solid.weight(); } }
反射
类在首次使用时才会被动态加载到JVM中。当程序第一次引用该类的静态成员时,就会触发这个类的加载。构造器是类的一个静态方法,尽管没有明确使用 static 关键词。因此,使用 new 操作符创建类的新对象也算作对该类静态成员的引用,构造器的初次使用会导致该类的加载。初始化被延迟到静态方法(构造器是隐式静态的)或非常量静态字段时。
使用 Class.newInstance() 创建的类必须有一个无参的构造器。Java 8以上推荐使用 Constructor.newInstance() 来代替。
在编写一个类时没有添加无参构造方法,那么编译器会自动添加无参构造方法;(如果自己手动添加构造函数,无论有参数或是没参数,默认构造函数都将无效。
加载三个步骤:
加载。这个由类加载器执行的。该步骤会先找到字节码(通常在类路径中的磁盘上,但也不一定),然后从这些字节码中创建一个 Class 对象。
链接。链接阶段又分为三个步骤:(1)验证:验证类中的字节码 。(2)准备:为静态字段分配存储空间(3)解析:在必要时解析该类对其他类的所有引用。
初始化。如果有基类的话,会先初始化基类,执行静态初始化器和静态初始化块。
关键字 instanceof
判断某个类的归属。
if(x instanceof Dog) ((Dog)x).bark();
动态代理
获取 Class对象的四种方式:
1. 知道具体类的情况下可以使用:
Class alunbarClass = TargetObject.class;
但是我们一般是不知道具体类的,基本都是通过遍历包下面的类来获取 Class 对象,通过此方式获取 Class 对象不会进行初始化
2. 通过 Class.forName()传入类的全路径获取:
Class alunbarClass1 = Class.forName("cn.javaguide.TargetObject");
3. 通过对象实例instance.getClass()获取:
TargetObject o = new TargetObject(); Class alunbarClass2 = o.getClass();
4. 通过类加载器xxxClassLoader.loadClass()传入类路径获取:
ClassLoader.getSystemClassLoader().loadClass("cn.javaguide.TargetObject");
通过类加载器获取 Class 对象不会进行初始化,意味着不进行包括初始化等一系列步骤,静态代码块和静态对象不会得到执行
注解
我们无法对@interface使用extends关键字。注解不能继承。
@Target
CONSTRUCTOR----构造器声明
FIELD---字段声明(包括枚举常量)
LOCAL_VARIABLE---本地变量声明
METHOD---方法声明
PACKAGE---包声明
PARAMETRE---参数声明
TYPE---类、接口(包括注解类型)或枚举的声明
@Retention
注解信息可以保存多久。可能的 RetentionPolicy 参数:
SOURCE---注解会被编译器丢弃
CLASS---注解在类文件中可杯编译器使用,但会被虚拟机丢弃
RUNTIME---注解在运行时仍被虚拟机保留,因此可以通过反射读取到注解信息
@DOcumented
在 Javadoc 中引入该注解
@Inherited
允许子类继承父注解
@Repeatable
可以多次应用于同一个声明