目录
反射
什么是反射
反射是Java系统的API,它允许程序在运行过程中取得任何一个已知名称的类的内部信息,包括其中的构造方法、声明的字段和定义的方法等
利用反射API可以实现动态执行:
动态加载类,获取信息
-动态创建对象
动态访问属性
-动态调用方法
动态执行:只是在JVM运行期间才确定的执行次序。
静态执行:是指编译以后就确定了程序的运行次序, JVM运行期间按照既定的次序执行。
反射API
Java反射API提供了动态执行能力ClassAPI :
- java.lang.Class类,用于加载类和获取类的相关信息
Java反射API位于java.lang.reflect包中。主要包括以下几类:
- Constructor类:用来描述一个类的构造方法。
- Field类:用来描述一个类的成员变量。
- Method类:用来描述一个类的方法。
- Modifier类:用来描述类内各元素的修饰符。
- Array :用来对数组进行操作。
package reflect; import java.lang.reflect.Method; import java.util.Scanner; /** * java 反射机制 * * 反射机制是一种动态机制,允许我们程序在运行期间确定实例化对象的操作,方法调用的操作和 * 属性赋值等操作。 * * 反射可以大大提高代码灵活度,但是也会带来更多的系统开销和降低运行性能。因此反射只在关 * 键地方使用而不能过度依赖。 * @author pc * */ public class ReflectDemo1 { public static void main(String[] args) throws ClassNotFoundException { /* * Class类 称为:类的类对象 * 该类的每一个实例用于表示已经被JVM加载的一个类,并且每个被JVM加载的类都有 * 且只有一个类对象与之关联。 * 通过类对象我们可以得知其表示的类的一切信息:类名,有哪些属性,方法,构造器 * 并可以获取它们以便在运行期间调用。 * * 所以反射的第一步就是获取要操作的类的类对象。 * * 获取一个类的类对象有三种方式: * 1:类名.class * 例如:String.class * ArrayList.class * * 2:Class.forName(String className) * Class提供了一个静态方法forName,可以根据类的完全限定名(包名.类名)形式 * 获取该类的类对象 * * 3:ClassLoader类加载器形式 */ //获取String的类对象 // Class cls = String.class;//优点是简单直接,但是缺点是硬编码获取 /* * 该方法会抛出ClassNotFoundException,当给定的类的完全限定名无效时会抛出 * 该异常。 */ // Class<?> cls = Class.forName("java.lang.String"); // Class<?> cls = Class.forName("reflect.Person"); /* * java.lang.String * java.util.ArrayList * java.util.HashSet * java.util.HashMap * java.io.FileOutputStream * reflect.Person * 以上可參考 */ System.out.println("请输入一个类名:"); Scanner scanner = new Scanner(System.in); String className = scanner.nextLine(); Class<?> cls = Class.forName(className); //获取类名 String name = cls.getName(); System.out.println(name); /* * Method类的每一个实例用于表示某个类中的某个方法 * * Class提供的方法: * Method[] getMethods() * 可以获取Class表示的类的所有公有方法 */ System.out.println("获取本类所有公有方法,包含从超类继承的方法"); Method[] methods= cls.getMethods(); for(Method m : methods) { System.out.println(m.getName()); } System.out.println("获取本类定义的所有方法(不含有继承的方法):"); methods = cls.getDeclaredMethods(); for(Method m : methods) { System.out.println(m.getName()); } } }
动态加载类
Class.forName( )
动态加载类到内存万法区:
Class cls = Class.forName(类名)
1.类名是运行期间动态输入的类名,可以任何类名
2.返回值是一个引用,利用这个引|用指向的对象可以访问方法区中的类信息。
3.如果类名是错误的将出现"类没有找到"的异常
![](https://img-blog.csdnimg.cn/20200614223241665.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L2MyMDIwMDM=,size_16,color_FFFFFF,t_70)
动态创建对象
newInstance( )
Class提供了动态创建对象的方法:
Object newInstance( )
- newInstance方法将调用类信息中的无参数构造器创建对象,如果没有无参数构造器,将抛出没有方法的异常。
- 如果需要调用有参数构造器,可以利用ConstructorAPI实现。
- 返回值弓|用动态创建的对象,因为可以是任何类型的对象,所以其类型为Object.
package reflect;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayList;
/**
* 使用反射机制实例化对象
* @author pc
*
*/
public class ReflectDemo2 {
public static void main(String[] args) throws ClassNotFoundException, InstantiationException, IllegalAccessException, NoSuchMethodException, SecurityException, IllegalArgumentException, InvocationTargetException {
//硬编码方式实例化
Person p = new Person();
System.out.println(p);
ArrayList a = new ArrayList();
System.out.println(a);
/*
* 使用反射机制:
* 1:加载要实例化对象的类的类对象
* 2:通过类对象的newInstance方法实例化
*/
//实例化Person
Class<?> cls = Class.forName("reflect.Person");
//需要注意newInstance方法要求Class表示的类必须有无参构造器
Object obj = cls.newInstance();
System.out.println(obj);
//硬编码方式调用有参数构造器
Student stu = new Student("张三",22);
System.out.println(stu);
/*
* 利用有参构造器实例化对象
* 1:加载要实例化的类的类对象
* 2:通过类对象获取指定的构造方法
* 3:利用该构造方法实例化
*/
//1
Class cls1 = Class.forName("reflect.Student");
//2 Student(String name, int age)
Constructor c = cls1.getConstructor(String.class,int.class);
Object o1 = c.newInstance("李四",18);
System.out.println(o1);
}
}
动态调用方法
动态发现方法
Class提供了方法可以动态获取类的全部方法信息:
Method[ getDeclaredMethods( )
- Method代表方法信息,可以利用Method API获取方法对详细信息,如:方法名,返回值类型,参数类型列表等。
- 这个方法返回对数组代表当前类中对全部方法信息,每个元素代表一个方法信息。
动态执行方法
Method提供了动态执行一个方法的方法:
Object invoke(Object obj, Object.. args)
- obj代表一个对象,该对象上一定包含当前方法!否则将出现调用异常;如果obj为nul则抛出空指针异常;
- args代表调用方 法时候传递的实际参数,如果没有参数可以不用或者传递null ,但是要注意参数的个数和类型必须和要调用的方法匹配,否则将出现参数错误异常;
- 返回值表示方法执行的结果 ,因为可能是任何类型,则其类型为Object ,调用没有返回值的方法则返回值为null ;
- 当被调用方法执行出现异常时候抛出Invoc ationTargetException。
package reflect; import java.lang.reflect.Method; import java.util.Scanner; /** * 利用反射调用方法 * @author pc * */ public class ReflectDemo3 { public static void main(String[] args) throws Exception { Person p = new Person(); p.sayHello(); /* * 利用反射调用方法: * 1:加载类对象 * 2:实例化 * 3:通过类对象获取要调用的方法 * 4:调用该方法 */ Scanner scanner = new Scanner(System.in); System.out.println("请输入类名:"); String className = scanner.nextLine(); System.out.println("请输入方法名:"); String methodName = scanner.nextLine(); //1 // Class<?> cls = Class.forName("reflect.Person"); Class<?> cls = Class.forName(className); //2 Person o = new Person(); Object o = cls.newInstance(); //3 获取Person中无参数的sayHello方法 // Method method = cls.getMethod("sayHello"); Method method = cls.getMethod(methodName); //4 o.sayHello() method.invoke(o); } }
执行不可访问方法
如果利用反射API调用了没有可访问权限时候会抛出异常:
IllegalAccessException,表示没有访问权限。
但是在Method方法上提供了解除访问限制的方法:
setAccessible(boolean flag)
在invoke之前使用这个方法可以解除访问限制,实现访问没有权限的方法。
注意:这个功能破坏了面向对象原有的封装性,利用它但是却能写出一些特殊功能代码,有些面试官也将这个知识点作为反射是否熟悉的标志!
package reflect;
import java.lang.reflect.Method;
/**
* 使用反射机制调用私有方法
* 这样做可能破坏类的封装性
* @author pc
*
*/
public class ReflectDemo5 {
public static void main(String[] args) throws Exception {
// Person p = new Person();
// p.dosome();//编译不通过
Class<?> cls = Class.forName("reflect.Person");
Object o = cls.newInstance();
//获取私有方法dosome
Method m = cls.getDeclaredMethod("dosome");
m.setAccessible(true);//强行访问!
m.invoke(o);
}
}
日期操作
Date及其常用API
Java中的时间
- Java中的时间使用标准类库的Date类表示,是用距离一个固定时间点的毫秒数(可正可负, long类型)表达一个特定的时间点;
- 固定的时间点叫纪元( epoch ), 是UTC时间1970年1月1日00:00:00 ;
- UTC ( Universal Time Coordinated世界调整时间)与GMT ( Greenwich Mean Time格林威治时间)一样,是一种具有实际目的的科学标准时间。
Date类简介
- java.util.Date类封装日期及时间信息。
- Date类的大多数用于进行时间分量计算的方法已经被Calendar取代。
Date date = new Date();
//系统当前的日期及时间信息
System.out.println(date);
// Sun Jan 06 11:52:55 CST 2013
long time = date.getTime);
//1970年1月1日至今的毫秒数
setTime和getTime方法
/**使用setTime和getTime设置及获取时间*/
public void testSetTime() {
Date date = new Date();
//输出当天此时此刻的日期和时间
System.out.println(date);
long time = date.getTime();
//增加一天所经历的毫秒数
time
+=
60 *
60
* 24 * 1000;
date. setTime(time);
//输出明天此时此刻的日期和时间
System.out.println(date);
/**
获取当前系统时间*/
public void testGetTime() {
Date date = new Date();
System.out.println(date);
//1970年1月1日零时距此刻的毫秒数
long time = date.getTime();
System.out.println(time);
Date重写toString
- Date重写了toString(方法,用一个字符串来描述当前Date对象所表示的时间。格式如下:
- Mon Feb 17 15:36:55 CST 2014
SimpleDateFormat
SimpleDateFormat简介
java.text.SimpleDateFormat是一个以与语言环境有关的方式来格式化和解析日期的具体类。它允许进行格式化(日期->文本)、解析(文本->日期)和规范化
- 构造方法
- SimpleDateFormat ( )
- -SimpleDateFormat ( String pattern ) //用给定的模式和默认语言环境的日期格式符号构造SimpleDateFormat
- 方法
- -final String format ( Date date) //Date =>String
- - Date parse(String source) throws ParseException //String => Date
日期模式匹配字符
将Date格式化为String
/**日期格式化*/
public void testFormat() {
SimpleDateFormat sdf = new SimpleDateFormat(' 'yy- MM-dd HH:mm:ss' );
Date date = new Date();
String dateStr = sdf.format(date);
System.out.println(dateStr);
}
将String格式化为Date
/**和format方法相反, parse方法用于按照特定格式将表示时间
的字符串转换为Date对象*/
public void testParse() throws Exception {
String str = "2013-12-25";
SimpleDateFormat sdf = new SimpleDateFormat( "yyy-MM-dd");
Date date = sdf.parse(str);
System.out.println(date);
}
Calendar
Calendar简介
- java.util.Calendar类用于封装日历信息,其主要作用在于其方法可以对时间分量进行运算;
- Calendar是抽象类,其具体子类针对不同国家的日历系统,其中应用最广泛的是GregorianCalendar (格里高里历,即通用的阳历) , 对应世界上绝大多数国家/地区使用的标准日历系统。
getInstance方法
Calendar提供了一个类方法getInstance ,以获得此类型的一个通用的对象
Calendar的getInstance方法返回一个Calendar对象其日历字段已由当前日期和时间初始化
Calendar C = Calendar.getInstance(;
/**使用alendar及子类获取时间*/
public void testGetInstance() {
Calendar C = Calendar.getInstance(;
//输出Calendar对象所属的实际类型
System.out.println(c.getClass).getName();
// getTime方法返回对应的Date对象
System.out.println(c.getTime();
//创建GregorianCalendar对象
GregorianC alendar c1 = new Gregorian alendar(2013, Calendar.DECEMBER,25);
System.out.println(c1.getTime();
}
设置日期及时间分量
/**设置日期及分量*/
public void testSet() {
Calendar C = Calendar.getInstance();
c.set(C alendar.YEAR, 2014);
c.set(C alendar.MONTH, Calendar.DEC EMBER);
c.set(Calendar.DATE, 25);
System.out. println(c.getTime());
//Thu Dec 25 16:02:08 CST 2014
c.set(C alendar.DATE, 32);
System.out.println(c.getTime());
//Thu Jan 01 16:02:08 CST 2015
}
获取日期及时间分量
使用Calendar提供的get方法及- -些常量可以获取日期及时间分量
- static int YEAR指示年份的字段数字
- static int MONTH指示月份的字段数字
- static int DATE指示一个月份中的第几天
- static int DAY OF WEEK 指示一个星期中的某天, 1为星期日
/**获取时间及分量*/
public void testGet() {
Calendar C = Calendar.getInstance();
C. set(C alendar.YEAR, 2014);
c.set(C alendar. MONTH, Calendar.DECEMBER);
C. set(Calendar.DATE, 25);
int dayOfWeek = c.get(Calendar.DAY_ OF_ _WEE K);
System.out.println(dayOfWeek);
//输出为5,表示周四,周日为每周的第1天.
}
getActualMaximum方法
int getActualMaximum ( int field )给定此Calenda的时间值,返回指定日历字段可能拥有的最大值,
例如:
int year = 2014;
Calendar C = Calendar.getInstance();
c.set(C alendar.YEAR, year);
c.set(C alendar.DATE, 1);
for (int month = Calendar.JANUARY;
month <= Calendar.DEC EMBER; month+ +)
C. set(C alendar.MONTH, month);
System.out.println(year +"年"
+(month+1)+"月: "
+ c.getActualMaximum (Calendar.DATE+ "天");
}
}
add方法
void add ( int field,int mount )为给定的时间分量的值加上给定的值,若给定的值为负数则是减去给定的值
/**输出一年后再减去3个月的日期*/
public void testAdd(){
Calendar calendar = Calendar.getInstance();
calendar.add(C alendar.YEAR, 1);/加一年
calendar.add(C alendar.MONTH, -3);//减3个月
System.out.println(" year:" + calendar.get( alendar.YEAR));
System.out.println(" month:" +(calendar.get(C alendar.MONTH)+ 1));
System.out.println(" day:" + calendar.get( alendar.DAY_ OF_ .MONTH));
}
setTime和getTime方法
- Date getTime( )使用Date描述Calendar表示的日期并返回
- void setTime(Date d)使Calendar表示Date所描述的日期
/**.通过Date对象设置日期,在获取日期*/
public void testsetTimeAndGetTime(){
Calendar calendar = Calendar.getInstance();
Date date = new Date();
calendar.setTime(date);
date = calendar.getTime();
}
Java8 Lambda
Lambda
Lambda表达式简介
- Lambda表达式是一种"匿名函数" (对Java而言这并不完全正确编译后还会转换为类) , 简单地说它是没有声明的方法:即没有访问修饰符、返回值声明和名字。
- Lambda表达式为Java添加了缺失的函数式编程特点。
Lambda表达式的结构
- Java中的Lambda表达式语法: (argument) -> {body}
- 一个Lambda表达式可以有零个或多个参数
- 参数的类型既可以明确声明, 也可以根据上下文来推断。
- 例如: (int a)与(a)效果相同
- 所有参数需包含在圆括号内,参数之间用逗号相隔。
- 例如:(a,b)或(inta,intb)或(Stringa,intb,floatc)
- 空圆括号代表参数集为空。
- -例如:()-> 42
- 只有一个参数,且其类型可推导时,圆括号( )可省略。
- -例如: a-> return a*a
- Lambda表达式的主体可包含零条或多条语句
- 如果Lambda表达式的主体只有一 条语句,花括号}可省略,如果返回值需要省略return. 如果Lambda表达式的主体包含一条以上语句,则表达式必须包含在花括号{}中(形成代码块)。
- 匿名函数的返回类型与代码块的返回类型一致,若没有返回则为空
(inta,intb)->{returna+b;}
() -> System. out. println("Hello World");
(String s) -> { System . out. println(s); }
() -> 42
() -> { return 3.1415 } ;
功能接口(函数接口)
- 函数式接口是只包含一个抽象方法声明的接口。
- java.lang.Runnable就是一种函数式接口 ,在Runnable接中只声明了一个方法void run() ,相似地,ActionListener接口也是一种函数式接口
- 般可以使用匿名内部类来实例化函数式接口的对象有了Lambda表达式,这一方式可以得到简化。
//通过Lambda表达式创建Runnable接口的实例:
Runnabler = () -> System.out.printIn("hello world ");
//相当于匿名内部类创建Runnable接口的实例:
Runnabler = newRunnable(){
public void run({
System.out.println("hello world");
}
};
//当不指明函数式接口时,编译器会自动解释这种转化:
Thread t = new Thread(
() -> System.out.printn("hello world")
);
/**Java编译器根据构造器参数 new Thread(Runnable r)
推断整个Lambda表达式的类型是Runnable类型的*/
@ FunctionalInterface
- @FunctionalInterface是Java 8新加入的一-种注解,用于指明该注解类型声明是根据Java语言规范定义的函数式接口。
- 当接口不是有效的函数式接口时,可以使用@FunctionalInterface注解时候将出现编译错误。
错误:
Unexpected @ FunctionalInterface annotation
@FunctionalInterface ^ WorkerInterface is not a functional
interface multiple
non-overriding abstract methods found in interface
WorkerInterface 1 error
Lambda表达式与匿名内部类
- 使用匿名类与Lambda表达式的一大区别在于关键词的使用。
- 对于匿名类,关键词this解读为匿名类,而对于Lambda表达式,关键词this解读为写就Lambda的外部类。
- Java编译器编译Lambda表达式并将他们转化为类里面的私有函数。