发布时间:2023-04-03 18:30
引入的目的:用于定义有限数量的一组同类常量,限定了使用者在使用时常量的取值范围。
Java中枚举的演变:
java1.5前使用类+final修饰变量+私有构造方法
来实现,外部无法使用构造方法创建对象,只能使用类名来使用定义好的常量。
java1.5引入了枚举类型,在定义的时候,简化了语法:
第一种方法(简化内部调用构造方法):
public enum Level {
//使用Enum方法比较大小时根据传入的数字比较
LOW(30), MEDIUM(15), HIGH(7), URGENT(1);
private int levelValue;
private Level(int levelValue) {
this.levelValue = levelValue;
}
public int getLevelValue() {
return levelValue;
}
}
第二种方法(直接写出枚举变量不传参)
public enum Level {
//使用Enum方法比较大小时根据先后顺序比较
LOW, MEDIUM, HIGH, URGENT;
}
枚举类型继承的是抽象类Enum
,对枚举的变量不能直接使用运算符操作,需要使用该抽象类的方法:
compareTo
方法进行比较,其比较的值也是根据序号值进行运算的。每个枚举对象,都可以实现自己的抽象方法:
自定义一个接口,然后让该枚举类型继承该接口:
interface LShow{
void show();
}
public enum Level implements LShow{}
可以在枚举中实现公共接口(没有单独实现的将调用公共方法),也可以对每个枚举对象单独实现接口:
interface LShow{
void show();
}
public enum Level implements LShow {
//比较大小时根据传入的数字比较
LOW{
@Override
public void show() {
System.out.println(\"LOW 实现方法\");
}
}, MEDIUM(){
@Override
public void show() {
System.out.println(\"MEDIUM 实现方法\");
}
}, HIGH;
@Override
public void show() {
System.out.println(\"公共实现方法\");
}
}
枚举注意事项:
java.lang.Enum类
而不是Object类
private
构造方法name
属性,因为自带name
属性set
方法,不符合枚举最初设计初衷。Java 注解(Annotation
)又称 Java 标注,是 JDK5.0 引入的一种注释机制,但不是注释!。
注解的应用场景:
常用的内置注解:
@Override
: 重写,定义在java.lang.Override
。@Deprecated
:废弃 ,定义在java.lang.Deprecated
@FunctionalInterface
: 函数式接口。
lambda
表达式的类型。@SuppressWarnings
:抑制编译时的警告信息。
定义在java.lang.SuppressWarnings
@SuppressWarnings(\"unchecked\")
抑制单类型的警告
@SuppressWarnings(\"unchecked\",\"rawtypes\")
抑制多类型的警告
@SuppressWarnings(\"all\")
抑制所有类型的警告
还有其他参数,例如装箱警告boxing
,空操作警告null
等等,具体可以查表。
生效的范围与编写的位置有关,可以针对变量、方法、类等进行注解。
元注解(用来配置注解):
@Retention
- 标识这个注解怎么保存,是只在代码中,还是编入class文件中,或者是在运行时可以通过反射访问。
@Documented
- 标记这些注解是否包含在用户文档中javadoc
。
@Target
- 标记这个注解应该是哪种 Java 成员。
@Inherited
- 标记这个注解是自动继承的:
@Inherited
修饰的注解@Inherited
修饰注解的整体架构:
注解少于两个参数
注解参数无参时,可以不传参数
注解参数为1个参数时,推荐定义默认的value属性,这样可以省略书写时vaule = \"xxxx\"
中的前面部分,直接写\"xxxx\"
即可。
注解参数传入超过1个时,不能省略,必须写全,例如:vaule = \"xxxx\",num=100
可以定义一个默认的参数,使用default xxx
,当不传入该参数,则默认使用定义的参数取值。
出入数组参数且长度大于1个时,需要添加大括号,例如:value = {\"x\",\"y\"}
@MyAnnotation(\"param\")
public class Demo {
public static void main(String[] args) {
}
}
@Documented
//限定MyAnnotation用于在类/接口/枚举、方法中进行注解
@Target({ElementType.TYPE,ElementType.METHOD})
//MyAnnotation注解 保存的策略 保存在class中且被JVM可读
@Retention(RetentionPolicy.RUNTIME)
//可以被子类继续继承MyAnnotation注解
@Inherited
@interface MyAnnotation{
String value();
int num() default 100;
}
JAVA反射机制是在运行状态中,获取任意一个类的结构 , 创建对象 , 得到方法,执行方法 , 属性。这种在运行状态动态获取信息以及动态调用对象方法的功能被称为java语言的反射机制。
类加载器:
引导启动类加载器(BootstrapClassLoader
)
C++语言
写内嵌在JVM
中的加载器,主要加载JAVA_HOME/lib
下类库,无法被程序员直接使用。扩展类加载器(ExtensionClassLoader
)
BootstrapClassLoader
,是由sun.misc.Launcher$ExtClassLoader
实现的,主要加载JAVA_HOME/lib/ext
目录中的类库。应用类加载器(App ClassLoader
)
classpath
目录下的所有jar和class文件。它的父加载器为ExtensionClassLoader
类加载的Java委派概念:
如果一个类加载器收到加载请求,它会把请求转交给父类加载器,如果父类加载器已经加载或者响应加载则子类加载器才会尝试自己去加载。
按需加载,第一次使用时才进行加载,且只加载一次。
于有了类加载器,Java运行时系统不需要知道文件与文件系统。
加载配置文件:
注意,在未设置resource root目录
前,默认从src目录
进行加载,否则从resource root
目录下加载配置文件,加载代码如下:
public class Test {
public static void main(String[] args) throws IOException {
InputStream is = Test.class.getClassLoader().
getResourceAsStream(\"config.txt\");
BufferedReader br = new BufferedReader(new InputStreamReader(is));
System.out.println(br.readLine());
}
}
配置resource root目录
方法:IDEA中右键选择某个目录,Mark Directory as Resources Root
即可。
加载类的三种方法,注意区别第三种和前两种的区别:
public static void main(String[] args) throws ClassNotFoundException {
//方法1:通过类名来加载类
Class<Person> c1 = Person.class;//class com.forwardxiang.Person
System.out.println(c1);
//方法2:通过类的对象来加载类
Person p = new Person();
Class<Person> c2 = (Class<Person>)p.getClass();
System.out.println(c1 == c2);//true,说明是同一块内存
//方法3:通过类的全名来加载类,无需通过创建类或对象来加载类
Class<Person> c3 = (Class<Person>)Class.forName(\"com.forwardxiang.Person\");
System.out.println(c1 == c3);//true,说明是同一块内存
//方法3的另一个不强转的情况下,可以在没有Person类的情况下编译通过,
// 只需要在运行到这行之前能够生成Person类即可,
// 也就是说可以单独编译该行代码无需Person参与
Class c4 = Class.forName(\"com.forwardxiang.Person\");
}
反射中的构造方法:注意getDeclaredConstructor
与c3.setAccessible(true)
的配合使用
Class pClass = Class.forName(\"com.forwardxiang.Person\");
//使用获取的类的无参构造方法
Constructor c1 = pClass.getConstructor();
Object p = c1.newInstance();
System.out.println(p);//类中有override toString方法
//使用获取的类的全参构造方法
Constructor c2 = pClass.getConstructor(String.class,int.class);
Object p2 = c2.newInstance(\"xiangwei\",18);
System.out.println(p2);//类中有override toString方法
//使用获取的类的私有构造方法 c3.setAccessible(true);
Constructor c3 = pClass.getDeclaredConstructor(String.class,int.class);
c3.setAccessible(true);
Object p3 = c3.newInstance(\"xiangwei\",18);
System.out.println(p3);//类中有override toString方法
反射中的方法:注意getDeclaredMethod
与ageSet.setAccessible(true)
的配合使用
Class pClass = Class.forName(\"com.forwardxiang.Person\");
//使用获取的类的无参构造方法
Constructor constructor = pClass.getConstructor();
Object o = constructor.newInstance();
System.out.println(o);
//获取类的方法
Method nameSet = pClass.getMethod(\"setName\", String.class);
nameSet.invoke(o,\"xiangwei\");
System.out.println(o);
//获取类的私有方法
Method ageSet = pClass.getDeclaredMethod(\"setAge\", int.class);
ageSet.setAccessible(true);
ageSet.invoke(o,22);
System.out.println(o);
反射中的属性:注意getDeclaredField
与namer.setAccessible(true)
的配合使用
//获取类属性
Field number = pClass.getField(\"num\");
number.set(o,10086);
System.out.println(o);
//获取类的私有属性
Field namer = pClass.getDeclaredField(\"name\");
namer.setAccessible(true);
namer.set(o,\"xiangwei\");
System.out.println(o);
反射与注解:
设置注解并传入值,以数据库表名称和表中的字段做注解为例:
@TableAnnotation(\"test_Book\")
public class Book {
@ColumnAnnotation(columnName = \"name\",type = \"varchar\",length = \"11\")
private String name;
@ColumnAnnotation(columnName = \"info\",type = \"varchar\",length = \"50\")
private String info;
@ColumnAnnotation(columnName = \"id\",type = \"int\",length = \"11\")
private int id;
//......此处省略
}
@Documented
@Target(ElementType.FIELD)
@Retention(RetentionPolicy.RUNTIME)
public @interface ColumnAnnotation {
String columnName();//描述列名
String type();//描述类型
String length();//描述长度
}
@Documented
@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
public @interface TableAnnotation {
String value();//用于标注类对应的表格名称
}
获取注解中传入的值:
Class pClass = Class.forName(\"com.forwardxiang.Book\");
TableAnnotation ta = (TableAnnotation) pClass.getAnnotation(TableAnnotation.class);
System.out.println(ta.value());//test_Book
Field[] fs = pClass.getDeclaredFields();
for (Field f:fs) {
ColumnAnnotation ca = f.getAnnotation(ColumnAnnotation.class);
System.out.println(f.getName()+\"属性,对应数据库中字段:\"+ca.columnName()+\" \"+ca.type()+\" \"+ca.length());
//name属性,对应数据库中字段:name varchar 11
//info属性,对应数据库中字段:info varchar 50
//id属性,对应数据库中字段:id int 11
}
内省:基于反射,Java提供一套应用到JavaBean的API,对于反射的操作,进行了封装 。
由于应用场景是bean对象,要求至少有:
boolean类型
对应的get方法
是is属性名
方法调用层次(内省使用起来比直接用反射要更容易):
Introspector--->getBeanInfo--->BeanInfo--->getPropertyDescriptors()--->
--->getReadMethod()--->Method(属性的get方法)
MethodDescriptor[]两个分支
--->getWriterMethod()--->Method(属性的set方法)
代码举例:
Class c = Express.class;
Constructor constructor = c.getConstructor();
Object o = constructor.newInstance();
BeanInfo bi = Introspector.getBeanInfo(c);
PropertyDescriptor[] pds = bi.getPropertyDescriptors();
for (PropertyDescriptor pd:pds) {
System.out.println(\"属性名称:\" + pd.getName() + \"\\t 属性类型:\" + pd.getPropertyType());
//由于属性排列顺序并非定义时的顺序 因此不要想当然直接用pds[]下标操作
if (Objects.equals(pd.getName(),\"address\")){
Method get = pd.getReadMethod();
Method set = pd.getWriteMethod();
set.invoke(o, \"shanghai\");
System.out.println(get.invoke(o));
}
}