第一部分、Junit单元测试
1)介绍
1.概述:Junit是一个单元测试框架,在一定程度上可以代替main方法,可以单独去执行一个方法,测试该方法是否能跑通,但是Junit是第三方工具,所以使用之前需要导入jar包
2)Junit的基本使用(重点啊)
1.导入Junit的jar包
2.定义一个方法,在方法上写注解: @Test
3.执行方法:
a.点击该方法左边的绿色按钮,点击run执行 -> 单独执行一个指定的方法
b.如果想要执行所有带@Test的方法,点击类名左边绿色按钮,点击run执行-> 执行当前类中所有@Test的方法
public class Demo01Junit {
@Test
public void add(){
System.out.println("我是@Test执行的add方法");
}
@Test
public void delete(){
System.out.println("我是@Test执行的delete方法");
}
}
3)Junit的注意事项
1.@Test不能修饰static方法
2.@Test不能修饰带参数方法
3.@Test不能修饰带返回值方法
4)Junit的相关注解
@Before:在@Test之前执行,有多少个@Test执行,@Before就执行多少次 -> 一般都是用作初始化变量
@After:在@Test之后执行,有多少个@Test执行,@After就执行多少次 -> 一般可以用作关闭资源
public class Demo01Junit {
@Test
public void add() {
System.out.println("我是@Test执行的add方法");
}
@Test
public void delete() {
System.out.println("我是@Test执行的delete方法");
}
@Before
public void methodBefore() {
System.out.println("我是@Before执行的方法");
}
@After
public void methodAfter() {
System.out.println("我是@After执行的方法");
}
}
5)@Test以后怎么使用
/**
* 此类专门去测我们写好的功能
*/
public class Demo02Junit {
/* public static void main(String[] args) {
CategoryController cc = new CategoryController();
int result = cc.add("蔬菜");
System.out.println("result = " + result);
List<String> list = cc.findAllCategory();
System.out.println(list);
}*/
/**
* 此方法专门测添加功能
*/
@Test
public void add(){
CategoryController cc = new CategoryController();
int result = cc.add("蔬菜");
System.out.println("result = " + result);
}
/**
* 此方法专门测查找功能
*/
@Test
public void find(){
CategoryController cc = new CategoryController();
List<String> list = cc.findAllCategory();
System.out.println(list);
}
}
public class CategoryController {
/**
* 添加功能
*/
public int add(String categoryName){
ArrayList<String> list = new ArrayList<>();
list.add(categoryName);
return 1;//如果返回一个1,证明添加成功了
}
/**
* 查找功能
*/
public List<String> findAllCategory(){
ArrayList<String> list = new ArrayList<>();
list.add("蔬菜");
list.add("水果");
list.add("服装");
list.add("电器");
list.add("玩具");
list.add("手机");
return list;
}
}
扩展注解:
@BeforeClass:在@Test之前执行,只执行一次,可以修饰静态方法
@AfterClass:在@Test之后执行,只执行一次,可以修饰静态方法
public class Demo03Junit {
@Test
public void add() {
System.out.println("我是@Test执行的add方法");
}
@Test
public void delete() {
System.out.println("我是@Test执行的delete方法");
}
@BeforeClass
public static void methodBefore() {
System.out.println("我是@Before执行的方法");
}
@AfterClass
public static void methodAfter() {
System.out.println("我是@After执行的方法");
}
}
第二部分、类的加载时机
1.new对象
2.new子类对象(new子类对象先初始化父类)
3.执行main方法
4.调用静态成员
5.反射,创建Class对象
1.类加载器(了解)_ClassLoader
1.概述:
在jvm中,负责将本地上的class文件加载到内存的对象_ClassLoader
2.分类:
BootStrapClassLoader:根类加载器->C语言写的,我们是获取不到的
也称之为引导类加载器,负责Java的核心类加载的
比如:System,String等
jre/lib/rt.jar下的类都是核心类
ExtClassLoader:扩展类加载器
负责jre的扩展目录中的jar包的加载
在jdk中jre的lib目录下的ext目录
AppClassLoader:系统类加载器
负责在jvm启动时加载来自java命令的class文件(自定义类),以及classPath环境变量所指定的jar包(第三方jar包)
不同的类加载器负责加载不同的类
3.三者的关系(从类加载机制层面):AppClassLoader的父类加载器是ExtClassLoader
ExtClassLoader的父类加载器是BootStrapClassLoader
但是:他们从代码级别上来看,没有子父类继承关系->他们都有一个共同的父类->ClassLoader4.获取类加载器对象:getClassLoader()是Class对象中的方法
类名.class.getClassLoader()
5.获取类加载器对象对应的父类加载器
ClassLoader类中的方法:ClassLoader
getParent()->没啥用
6.双亲委派(全盘负责委托机制)a.Person类中有一个String
Person本身是AppClassLoader加载
String是BootStrapClassLoader加载
b.加载顺序:
Person本身是App加载,按道理来说String也是App加载
但是App加载String的时候,先问一问Ext,说:Ext你加载这个String吗?
Ext说:我不加载,我负责加载的是扩展类,但是app你别着急,我问问我爹去->boot
Ext说:boot,你加载String吗?
boot说:正好我加载核心类,行吧,我加载吧!
=======================================================================
比如:
class Test{
new Person()
}a.Test是app加载,person按理来说也是app加载,但是app先问ext要不要加载
ext说不负责加载自定义类,我找boot去,boot一看,我不负责加载自定义类->perosn
app一看,两个爹都不加载,我自己加载
b.结论:当一个类加载器加载类的时候,总会先去上一级问一问,问上一级要不要加载,如果上级不加载,才自己加载
=======================================================================
7.类加载器的cache(缓存)机制(扩展):一个类加载到内存之后,缓存中也会保存一份儿,后面如果再使用此类,如果缓存中保存了这个类,就直接返回他,如果没有才加载这个类.下一次如果有其他类在使用的时候就不会重新加载了,直接去缓存中拿,保证了类在内存中的唯一性
8.所以:类加载器的双亲委派和缓存机制共同造就了加载类的特点:保证了类在内存中的唯一性
public class Demo01ClassLoader {
public static void main(String[] args) {
app();
//ext();
//boot();
}
/**
* 负责加载核心类
* BootStrapClassLoader:C语言编写,我们是获取不到的
*/
private static void boot() {
ClassLoader classLoader = String.class.getClassLoader();
System.out.println("classLoader = " + classLoader);
}
/**
* 负责加载扩展类
*/
private static void ext() {
ClassLoader classLoader = DNSNameService.class.getClassLoader();
System.out.println("classLoader = " + classLoader);
}
/**
* 负责加载自定义类
*/
private static void app() {
ClassLoader classLoader = Demo01ClassLoader.class.getClassLoader();
System.out.println("classLoader = " + classLoader);
ClassLoader parent = classLoader.getParent();
System.out.println("parent = " + parent);
ClassLoader parent1 = parent.getParent();
System.out.println("parent1 = " + parent1);//null
}
}
第三部分、反射
1)class类的以及class对象的介绍以及反射介绍
1.反射:是一种解剖class对象的技术
2.能解剖出点啥来?
a.成员变量 -> 赋值取值
b.构造方法 -> new对象
c.成员方法 -> 调用执行3.反射的作用:写出来的代码更灵活,通用
4.怎么学反射:先把反射技术看成是一套纯API来学
根据涛哥设计的案例去体会反射代码的通用性5.反射是解剖class对象的,所以玩儿反射第一步要干啥?
获取class对象
6.class对象:class文件对应的对象
class类:描述class对象的类叫做class类
2)反射之获取Class对象
1.方式1:调用Object中的getClass()方法:
Class<?> getClass()
2.方式2:不管是基本类型还是引用类型,jvm都为其提供了一个静态成员:class
3.方式3:Class类中的静态方法:
static Class<?> forName(String className)
className:类的全限定名(包名.类名)
public class Person {
private String name;
private Integer age;
public Person() {
}
public Person(String name, Integer age) {
this.name = name;
this.age = age;
}
//私有构造
private Person(String name){
this.name = name;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Integer getAge() {
return age;
}
public void setAge(Integer age) {
this.age = age;
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
}
public class Test01 {
public static void main(String[] args)throws Exception {
/*
1.方式1:调用Object中的getClass()方法:
Class<?> getClass()
*/
Person person = new Person();
Class aClass = person.getClass();
System.out.println("aClass = " + aClass);
System.out.println("===================");
//2.方式2:不管是基本类型还是引用类型,jvm都为其提供了一个静态成员:class
Class<Person> aClass1 = Person.class;
System.out.println("aClass1 = " + aClass1);
System.out.println("===================");
/*
3.方式3:Class类中的静态方法:
static Class<?> forName(String className)
className:类的全限定名(包名.类名)
*/
Class<?> aClass2 = Class.forName("com.atguigu.c_reflect.Person");
System.out.println("aClass2 = " + aClass2);
System.out.println("===================");
//System.out.println(aClass1==aClass2);
}
}
三种获取Class对象的方式最通用的一种
1.static Class<?> forName(String className)
className:类的全限定名(包名.类名)
2.原因:参数为String形式,可以和Properties文件结合使用
className=com.atguigu.c_reflect.Student
public class Test02 {
public static void main(String[] args)throws Exception {
Properties properties = new Properties();
FileInputStream in = new FileInputStream("day22_reflect\\pro.properties");
properties.load(in);
String className = properties.getProperty("className");
Class<?> aClass = Class.forName(className);
System.out.println("aClass = " + aClass);
}
}
3)获取Class对象中的构造方法
(1)获取所有public中的构造方法
1.Class类中的方法:
Constructor<?>[] getConstructors() -> 获取的所有public的构造
public class Test02 {
public static void main(String[] args)throws Exception {
Properties properties = new Properties();
FileInputStream in = new FileInputStream("day22_reflect\\pro.properties");
properties.load(in);
String className = properties.getProperty("className");
Class<?> aClass = Class.forName(className);
System.out.println("aClass = " + aClass);
}
}
(2)获取空参构造_public
1.Class类中的方法:
Constructor<T> getConstructor(Class<?>... parameterTypes)->获取指定的public的构造
parameterTypes:可变参数,可以传递0或多个参数
a.如果获取的是空参构造:参数不用写
b.如果获取的是有参构造:参数写参数类型的class对象
2.Constructor类中的方法:
T newInstance(Object... initargs) -> 创建对象
initargs:传递的是构造方法的实参
a.如果根据空参构造new对象,initargs不写了
b.如果根据有参构造new对象,initargs传递实参
public class Test03_GetConstructor {
public static void main(String[] args) throws Exception {
Class<Person> personClass = Person.class;
Constructor<Person> constructor = personClass.getConstructor();
System.out.println("constructor = " + constructor);
/*
好比是: Person person = new Person();
*/
Person person = constructor.newInstance();
//好比是:直接输出对象名,默认调用toString
System.out.println(person);
}
}
(3)利用空参构造创建对象的快捷方式_public
Class类中的方法:
T newInstance() -> 根据空参构造new对象
前提:被反射的类中必须有public的空参构造
public class Test04_GetConstructor {
public static void main(String[] args) throws Exception {
Class<Person> personClass = Person.class;
Person person = personClass.newInstance();
System.out.println(person);
}
}
(4)利用反射获取有参构造并创建对象_public
1.Class类中的方法:
Constructor<T> getConstructor(Class<?>... parameterTypes)->获取指定的public的构造
parameterTypes:可变参数,可以传递0或多个参数
a.如果获取的是空参构造:参数不用写
b.如果获取的是有参构造:参数写参数类型的class对象
2.Constructor类中的方法:
T newInstance(Object... initargs) -> 创建对象
initargs:传递的是构造方法的实参
a.如果根据空参构造new对象,initargs不写了
b.如果根据有参构造new对象,initargs传递实参
public class Test05_GetConstructor {
public static void main(String[] args) throws Exception {
Class<Person> class1 = Person.class;
Constructor<Person> constructor = class1.getConstructor(String.class, Integer.class);
System.out.println("constructor = " + constructor);
/*
好比是:Person person = new Person("柳岩",36);
*/
Person person = constructor.newInstance("柳岩", 36);
//好比是直接输出Person对象,默认调用toString
System.out.println(person);
}
}
(5)利用反射获取私有构造(暴力反射)
1.Constructor<?>[] getDeclaredConstructors() -> 获取所有构造方法,包括private和public
2.Constructor<T> getDeclaredConstructor(Class<?>... parameterTypes) -> 获取指定构造,包括private和public
parameterTypes:参数类型的class对象
3.Constructor有一个父类叫做AccessibleObject,里面有一个方法:
void setAccessible(boolean flag) -> 修改访问权限
flag为true-> 解除私有权限
public class Test06_GetConstructor {
public static void main(String[] args) {
Class<Person> personClass = Person.class;
Constructor<?>[] declaredConstructors = personClass.getDeclaredConstructors();
for (Constructor<?> declaredConstructor : declaredConstructors) {
System.out.println(declaredConstructor);
}
}
}
public class Test07_GetConstructor {
public static void main(String[] args)throws Exception {
Class<Person> personClass = Person.class;
Constructor<Person> ds = personClass.getDeclaredConstructor(String.class);
//解除私有权限 -> 暴力反射
ds.setAccessible(true);
Person person = ds.newInstance("曼曼");
System.out.println(person);
}
}
4)反射方法
(1)利用反射获取所有成员方法_public
1.Class类中的方法:
Method[] getMethods() -> 获取所有的public的方法,包括父类中的public方法
public class Test08_GetMethod {
public static void main(String[] args) {
Class<Person> personClass = Person.class;
Method[] methods = personClass.getMethods();
for (Method method : methods) {
System.out.println(method);
}
}
}
(2)反射之获取方法(有参,无参)
1.Class类中的方法:
Method getMethod(String name, Class<?>... parameterTypes)->获取指定的public的成员方法
name:传递方法名
parameterTypes:方法参数类型的class对象
2.Method中的方法:
Object invoke(Object obj, Object... args) -> 执行方法
obj:根据构造new出来的对象
args:方法实参
返回值:Object -> 接收被执行方法的返回值的,如果方法没有返回值,不用接收了
public class Test09_GetMethod {
public static void main(String[] args)throws Exception {
Class<Person> personClass = Person.class;
Person person = personClass.newInstance();
Method setName = personClass.getMethod("setName", String.class);
/*
相当于:person.setName("柳岩")
*/
setName.invoke(person,"柳岩");
System.out.println(person);
System.out.println("================================================");
Method getName = personClass.getMethod("getName");
//好比是:Object o = person.getName()
Object o = getName.invoke(person);
System.out.println("o = " + o);
}
}
反射小练习
<select id="findAll" class="类的全限定名">
select * from 表名
</select>public List<泛型类型> findAll();
需求:在配置文件中,配置类的全限定名,以及方法名,通过解析配置文件,让配置好的方法执行起来
className=com.atguigu.d_reflect.Person
methodName=eat
步骤:
1.创建properties配置文件,配置信息
a.问题:properties配置文件放到哪里?
将来我们开发完之后我们给用户的是out路径下的class文件,如果将配置文件直接放到模块下,那么out路径下是不会生成配置文件的,如果没有配置文件,程序也运行不起来
所以我们将配置文件可以放到src下,放到src下,out路径下就会自动生成配置文件
b.问题:将配置文件放到src下,out路径下会自动生成该配置文件,但是如果我们将所有的配置文件都放到src下,那么src会显得非常乱
所以我们可以单独去创建一个文件夹,将所有配置文件放到此文件夹下,将此文件夹改成资源目录resources
2.读取配置文件,解析配置文件
a.问题:如果将配置文件放到resources资源目录下,我们怎么读取
new FileInputStream("模块名\\resources\\properties文件名")-> 这样不行,out下没有resources
b.问题解决:用类加载器
ClassLoader classLoader = 当前类.class.getClassLoader()
InputStream in = classLoader.getResourceAsStream("文件名称"); //自动扫描resource下的文件(可以简单理解为扫描out路径下的配置文件)
3.根据解析出来的className创建class对象
4.根据解析出来的methodName,获取对应的方法
5.执行方法
className=com.atguigu.d_reflect.Person
methodName=eat
public class Test01 {
public static void main(String[] args)throws Exception {
//1.创建Properties集合
Properties properties = new Properties();
//2.读取配置文件
InputStream in = Test01.class.getClassLoader().getResourceAsStream("pro.properties");
//3.将流中的数据加载到集合中
properties.load(in);
//4.获取读取到的配置文件中的信息
String className = properties.getProperty("className");
String methodName = properties.getProperty("methodName");
//5.根据className获取class对象
Class<?> aClass = Class.forName(className);
Object o = aClass.newInstance();
//6.根据methodName获取方法
Method method = aClass.getMethod(methodName);
//7.执行被反射出来的方法
method.invoke(o);
}
}
第四部分、注解
1)注解的基本介绍
1.引用数据类型:
类 数组 接口 枚举 注解
1.jdk1.5版本的新特性->一个引用数据类型
和类,接口,枚举是同一个层次的
引用数据类型:类 数组 接口 枚举 注解
2.作用:
说明:对代码进行说明,生成doc文档(API文档)
检查:检查代码是否符合条件 @Override(会用) @FunctionalInterface
分析:对代码进行分析,起到了代替配置文件的作用(会用)
3.JDK中的注解:
@Override -> 检测此方法是否为重写方法
jdk1.5版本,支持父类的方法重写
jdk1.6版本,支持接口的方法重写
@Deprecated -> 方法已经过时,不推荐使用
调用方法的时候,方法上会有横线,但是能用
@SuppressWarnings->消除警告 @SuppressWarnings("all")
public class Person {
@Deprecated
public void eat(){
System.out.println("人要干饭");
}
}
@SuppressWarnings("all")
public class Test01 {
public static void main(String[] args) {
Person person = new Person();
person.eat();
System.out.println("=================");
ArrayList list = new ArrayList();
list.add("1");
}
}
2)注解的定义以及属性的定义格式
1.定义:
public @interface 注解名{
}2.定义属性:增强注解的作用
数据类型 属性名() -> 此属性没有默认值,需要在使用注解的时候为其赋值
数据类型 属性名() default 值 -> 此属性有默认值,如果有需要,也可以二次赋值
3.注解中能定义什么类型的属性呢?
a.8种基本类型
b.String类型,class类型,枚举类型,注解类型
c.以上类型的一维数组
public @interface Book {
//书名
String bookName();
//作者
String[] author();
//价格
double price();
//数量
int count() default 10;
}
3)注解的使用(重点)
1.注解的使用:
说白了就是为注解中的属性赋值
2.使用位置上:
在类上使用,方法上使用,成员变量上使用,局部变量上使用,参数位置使用等
3.使用格式:
a.@注解名(属性名 = 值,属性名 = 值...)
b.如果属性中有数组:
@注解名(属性名 = {元素1,元素2})
public @interface Book {
//书名
String bookName();
//作者
String[] author();
//价格
double price();
//数量
int count() default 10;
}
@Book(bookName = "金瓶梅",author = {"涛哥","金莲"},price = 9.9,count = 200)
public class BookShelf {
}
注解注意事项:
1.空注解可以直接使用->空注解就是注解中没有任何的属性
2.不同的位置可以使用一样的注解,但是同样的位置不能使用一样的注解
3.使用注解时,如果此注解中有属性,注解中的属性一定要赋值,如果有多个属性,用,隔开
如果注解中的属性有数组,使用{}
4.如果注解中的属性值有默认值,那么我们不必要写,也不用重新赋值,反之必须写上
5.如果注解中只有一个属性,并且属性名叫value,那么使用注解的时候,属性名不用写,直接写值
(包括单个类型,还包括数组)
4)注解解析的方法->AnnotatedElement接口
1.注解解析涉及的接口:
AnnotatedElement接口:
实现类:AccessibleObject, Class, Constructor, Field, Method
2.解析思路: 先判断指定位置上有没有使用指定的注解,如果有,获取指定的注解,获取注解中的属性值
a.boolean isAnnotationPresent(Class<? extends Annotation> annotationClass)->判断指定位置上有没有指定的注解
比如:判断BookShelf上有没有Book注解
Class bookShelf = BookShelf.class
bookShelf.isAnnotationPresent(Book.class)
b.getAnnotation(Class<T> annotationClass)-> 获取指定的注解
比如:获取BookShelf上的Book注解
Class bookShelf = BookShelf.class
boolean result = bookShelf.isAnnotationPresent(Book.class)
Book book = bookShelf.getAnnotation(Book.class)
public @interface Book {
//书名
String bookName();
//作者
String[] author();
//价格
double price();
//数量
int count() default 10;
}
@Book(bookName = "金瓶梅", author = {"涛哥", "金莲"}, price = 9.9, count = 200)
public class BookShelf {
}
public class Test01 {
public static void main(String[] args) {
Class<BookShelf> bookShelfClass = BookShelf.class;
boolean b = bookShelfClass.isAnnotationPresent(Book.class);
//System.out.println(b);
if (b){
Book book = bookShelfClass.getAnnotation(Book.class);
System.out.println(book.bookName());
System.out.println(Arrays.toString(book.author()));
System.out.println(book.price());
System.out.println(book.count());
}
}
}
以上代码没解析出来:
涛哥猜想:如果Book注解被加载到内存中了,那么我们一定是能判断出来BookShelf上有没有Book注解的,但是现在没有判断出来,但是 BookShelf上确实用了Book注解了,所以涛哥猜想,Book注解有可能就没有在内存中出现
第五部分、元注解
1.概述:元注解是管理注解的注解
2.从哪些方面管理呢?
a.控制自定义注解的使用位置
控制自定义注解是否能在类中使用
控制自定义注解是否能在方法上使用
控制自定义注解是否能在成员变量上使用等
b.控制自定义注解的生命周期(加载位置):
控制自定义注解是否能在源码中出现
控制自定义注解是否能在class文件中出现
控制自定义注解是否能在内存中出现
3.怎么使用:
a.@Target:控制自定义注解的使用位置
属性:ElementType[] value();
ElementType是一个枚举,里面的枚举类型可以类名直接调用
ElementType中的枚举:
TYPE:控制注解能使用在类上
FIELD:控制注解能使用在属性上
METHOD:控制注解能使用在方法上
PARAMETER:控制注解能使用在参数上
CONSTRUCTOR:控制注解能使用在构造上
LOCAL_VARIABLE:控制注解能使用在局部变量上
b.@Retention:控制自定义注解的生命周期(加载位置)
属性:RetentionPolicy value()
RetentionPolicy是一个枚举类,里面的枚举可以类名直接调用
RetentionPolicy中的枚举:
SOURCE:控制注解能在源码中出现 -> 默认
CLASS:控制注解能在class文件中出现
RUNTIME:控制注解能在内存中出现
@Target({ElementType.METHOD,ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface Book {
//书名
String bookName();
//作者
String[] author();
//价格
double price();
//数量
int count() default 10;
}
注解再次解析(成功了)
public class Test01 {
public static void main(String[] args) {
Class<BookShelf> bookShelfClass = BookShelf.class;
boolean b = bookShelfClass.isAnnotationPresent(Book.class);
//System.out.println(b);
if (b){
Book book = bookShelfClass.getAnnotation(Book.class);
System.out.println(book.bookName());
System.out.println(Arrays.toString(book.author()));
System.out.println(book.price());
System.out.println(book.count());
}
}
}