Java进阶:增强for循环、枚举、反射、泛型、动态代理等

时间:2022-12-16 19:24:31

1、静态导入

mport语句可以导入一个类或某个包中的所有类

import static语句导入一个类中的某个静态方法或所有静态方法

语法举例: 

import static java.lang.Math.sin;

import static java.lang.Math.*; 


2、可变参数

只能出现在参数列表的最后;

...位于变量类型和变量名之间,前后有无空格都可以;

调用可变参数的方法时,编译器为该可变参数隐含创建一个数组,在方法体中以数组的形式访问可变参数。


3、增强for循环

增强for循环可以用在数组、或实现Iterator接口的集合类上

语法格式:                                              

for(变量类型 变量 :需迭代的数组或集合){

   ......

}

例:用在数组上

int arr[] = new int[5];
for(int num : arr){
num = 10;
}
System.out.println(arr[0]);

用在List上:
List<String> list = new ArrayList<String>();
list.add("xxx");
for(String str : list){
str = "zhouyong";
}
System.out.println(list.get(0));

4、自动拆箱、自动装箱

JDK5.0的语法允许开发人员把一个基本数据类型直接赋给对应的包装类变量, 或者赋给 Object 类型的变量,这个过程称之为自动装箱。

如:Integer num1 = 12;

自动拆箱与自动装箱与之相反,即把包装类对象直接赋给一个对应的基本类型变量。

如:System.out.println(num1 + 12);


典型应用:

List list = new ArrayList();

list.add(1);

int j = (Integer)list.get(0);


5、枚举

枚举类也是一种特殊形式的Java类。

枚举类中声明的每一个枚举值代表枚举类的一个实例对象。

与java中的普通类一样,在声明枚举类时,也可以声明属性、方法和构造函数,但枚举类的构造函数必须为私有的(这点不难理解)。

枚举类也可以实现接口、或继承抽象类。

JDK5中扩展了swith语句,它除了可以接收int, byte, char, short外,还可以接收一个枚举类型。

若枚举类只有一个枚举值,则可以当作单态设计模式使用。

Java中声明的枚举类,均是java.lang.Enum类的子类,它继承了Enum类的所有方法。

常用方法:

name()

ordinal()

valueof(Class enumClass, String name)

values() 此方法虽然在JDK文档中查找不到,但每个枚举类都具有该方法,它遍历枚举类的所有枚举值非常方便。例:

public class Demo{

/**用JDK5的枚举解决
* @param args
*/
public static void main(String[] args) {
Student s = new Student();

s.setGrade(Grade.C);

System.out.println(s.getGrade().getValue());
Grade.B.run();
}
}

class Student{
private String name;
private Grade grade;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Grade getGrade() {
return grade;
}
public void setGrade(Grade grade) {
this.grade = grade;
}
}


//自定义枚举,如何去封装更多的信息
enum Grade{ //就是一个类
A("100-90"){
public void run(){
System.out.println("arun");
}
}
,B("89-80"){
public void run(){
System.out.println("brun");
}
}
,C("79-70"){
public void run(){
System.out.println("crun");
}
}
,D("69-60"){
public void run(){
System.out.println("drun");
}
}
,E("59-0"){
public void run(){
System.out.println("erun");
}
};

//枚举类的每一个枚举值,实际上就是枚举类的一个实例对象
private String value;
private Grade(String value){
this.value = value;
}
public String getValue() {
return value;
}

public abstract void run();
}


6、反射

反射是一门技术:可以通过反射拿到运行时期间的类。

反射就是把Java类中的各种成分映射成一个个的java对象。例如,一个类有:成员变量,方法,构造方法,包等等信息,利用反射技术可以对一个类进行解剖,把各个组成部分映射成一个个对象。

把Java的类、类的属性、方法、构造函数都看成对象 

关于反射用到的类、接口都在java.lang.reflect包中

例:

import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;

class Person{
private String name;
private int age=0;
public Person(){

}
public Person(String name){
this.name=name;
}
public Person(String name,int age){
this.age=age;
this.name=name;
}

public void run(){
System.out.println(name+" id running!!!!");
}
public void run(String name){
System.out.println(name+" is singing!!!!");
}
public void run(String name,int num){
System.out.println(num+"号"+name+" is singing!!!!");
}
}

public class ReflectTest{
public static void main(String args[]) throws ClassNotFoundException, SecurityException, NoSuchFieldException, NoSuchMethodException, IllegalArgumentException, IllegalAccessException, InvocationTargetException{
Person p=new Person("zhangsan",19);

//拿到类可以有三种方式:的到.class中类的信息
//1.通过对象继承Object的getClass方法来获得对象的类型
//Class 类的实例表示正在运行的 Java 应用程序中的类和接口。枚举是一种类,
//注释是一种接口。每个数组属于被映射为 Class 对象的一个类,所有具有相同
//元素类型和维数的数组都共享该 Class 对象。基本的 Java 类型(boolean、
//byte、char、short、int、long、float 和 double)和
<span style="white-space:pre"></span>//关键字 void 也表示为 Class 对象。
Class clas=p.getClass();//这样得到的Class是Object对象,即Person.class

//2.通过代表类型的字符串来获得,有异常,因为可能找不到指定名的类,
// 并且此方法必须加上包名,以便区分不同包下同名的类
Class clas2=Class.forName("Person");

//3.直接通过类名.class的方式来获得
Class clas3=Person.class;
System.out.println(clas3);//打印出的内存名是包括包名在内的全名

//基本数据类型也是Class对象,也是类型
Class intclas=int.class;
System.out.println(intclas.getName());//基本类型的名称打印出来没有包名

//得到类的指定属性:Class的getDcelaredFiled方法可以得到,dcelare是声明的意思
// getFiled方法只能得到各有类型的字段
Field ageField=clas.getDeclaredField("age");
System.out.println(ageField.getType());//打印出该属性的类型

//遍历类的所有属性:
Field[] fields=clas.getDeclaredFields();//得到存所有属性的数组
for(Field f:fields){
System.out.println("属性名为:"+f.getName()+"\t类型为:"+f.getType());
}
System.out.println("****************************");

//得到指定名称的方法:
Method m=clas.getMethod("run",String.class,int.class);
System.out.println("方法名:"+m.getName());

//得到方法的所有参数类型
Class[] parameterTypes=m.getParameterTypes();
System.out.print("方法的参数类型为:");
for(Class pt:parameterTypes)
System.out.print(pt.getName());

//得到参数的返回值类型
System.out.println();
System.out.println("返回值类型为:"+m.getReturnType());
System.out.println("****************************");

//执行方法,需要指明哪个对象来调用方法,并正确传参
m.invoke(p,"ping",150);
System.out.println("****************************");

//遍历出类声明的所有方法,包括private的
Method[]methods=clas.getDeclaredMethods();
for(Method method:methods){
System.out.println("方法名为:"+method.getName());
Class[] methodParameterTypes=method.getParameterTypes();
System.out.println("参数类型为:");
for(Class mpt:methodParameterTypes)
System.out.print(mpt.getName()+"\t");
System.out.println();

}
}
}


7、内省

访问JavaBean属性的两种方式:

直接调用bean的setXXX或getXXX方法。

通过内省技术访问(java.beans包提供了内省的API),内省技术访问也提供了两种方式。

通过PropertyDescriptor类操作Bean的属性

通过Introspector类获得Bean对象的 BeanInfo,然后通过 BeanInfo 来获取属性的描述器( PropertyDescriptor ),通过这个属性描述器就可以获取某个属性对应的getter/setter 方法,然后通过反射机制来调用这些方法。

public class Person {

private String username;
private String password;
private int age;
private Date birthday;


public String getUsername() {
return username;
}
public void setUsername(String username) { //username
this.username = username;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public Date getBirthday() {
return birthday;
}
public void setBirthday(Date birthday) {
this.birthday = birthday;
}

}

</pre><pre name="code" class="java">import java.beans.BeanInfo;
import java.beans.IntrospectionException;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import org.junit.Test;

public class Demo1 {
/**
* @param args
* @throws IntrospectionException
* @throws InvocationTargetException
* @throws IllegalAccessException
* @throws IllegalArgumentException
*/
public static void main(String[] args) throws IntrospectionException, IllegalArgumentException, IllegalAccessException, InvocationTargetException {
}

@Test
public void test2() throws IntrospectionException{
BeanInfo info = Introspector.getBeanInfo(Person.class);
PropertyDescriptor pds[] = info.getPropertyDescriptors();
for(PropertyDescriptor pd : pds){
String name = pd.getName();
System.out.println(name);
}
}

public void test1() throws Exception{
//传统方式访问bean的属性
Person p = new Person();
p.setAge(23);
p.getAge();


//采用内省技术操作bean的属性
Person p1 = new Person();

Class clazz = Person.class;
PropertyDescriptor pd = new PropertyDescriptor("username",clazz); //setUsername

Method m = pd.getWriteMethod(); //m代表的就是username属性对应的写方法setUsername(String username)
m.invoke(p1, "laohu");

m = pd.getReadMethod(); //m代表的就是username属性对应的读方法getUsername()
System.out.println(m.invoke(p1, null));

}

}
8、泛型

对象保存到集合中就会失去其特性,取出时通常要程序员手工进行类型的强制转换,这样不可避免就会引发程序的一些安全性问题。泛型解决了这个问题。

JDK5中的泛形允许程序员在编写集合代码时,就限制集合的处理类型,从而把原来程序运行时可能发生问题,转变为编译时的问题,以此提高程序的可读性和稳定性(尤其在大型程序中更为突出)。泛型是提供给javac编译器使用的,它用于限定集合的输入类型,让编译器在源代码级别上,即挡住向集合中插入非法数据。但编译器编译完带有泛形的java程序后,生成的class文件中将不再带有泛形信息,以此使程序运行效率不受到影响,这个过程称之为“擦除”。

例:泛型典型应用

import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.junit.Test;

public class Demo1 {

/**
* @param args
*/
public static void main(String[] args) {

List<Integer> list = new ArrayList<Integer>();
list.add(1);

Integer i = list.get(0);

}

//泛形的典型应用1
public void test1(){

List<Integer> list = new ArrayList<Integer>();
list.add(1);
list.add(2);

Iterator<Integer> it = list.iterator();
while(it.hasNext()){
int num = it.next();
System.out.println(num);
}

}

//泛形的典型应用2
public void test2(){

List<Integer> list = new ArrayList<Integer>();
list.add(1);
list.add(2);

for(Integer i : list){
System.out.println(i);
}

}

//泛形的典型应用3 //HashMap里的关键字不能重复,但值可以重复
@Test
public void test3(){

Map<String,String> map = new HashMap<String,String>();
map.put("1", "aa");
map.put("2", "bb");
map.put("3", "cc");

Set<Map.Entry<String, String>> set = map.entrySet();
for(Map.Entry<String,String> me : set){
System.out.println(me.getKey() + "=" + me.getValue());
}
}

@Test
public void test4(){
Map<String,String> map = new LinkedHashMap<String,String>();
map.put("1", "aa");
map.put("2", "bb");
map.put("3", "cc");

Set<Map.Entry<String, String>> set = map.entrySet();
for(Map.Entry<String,String> me : set){
System.out.println(me.getKey() + "=" + me.getValue());
}
}

}


9、动态代理

代理类主要负责为委托类预处理消息、过滤消息、把消息转发给委托类,以及事后处理消息等。

代理类与委托类之间通常会存在关联关系,一个代理类的对象与一个委托类的对象关联,代理类的对象本身并不真正实现服务,而是通过调用委托类的对象的相关方法,来提供特定的服务。 

按照代理的创建时期,代理类可以分为两种。 

静态代理:由程序员创建或特定工具自动生成源代码,再对其编译。在程序运行前,代理类的.class文件就已经存在了。 

动态代理:在程序运行时,运用反射机制动态创建而成。 

Java提供了一个Proxy类,调用它的newInstance方法可以生成某个对象的代理对象,使用该方法生成代理对象时,需要三个参数:

1)生成代理对象使用哪个类装载器

2)生成哪个对象的代理对象,通过接口指定

3)生成的代理对象的方法里干什么事,由开发人员编写handler接口的实现来指定

例:
public class ProxyTest {
public static void main(String[] args) throws Exception{
//创建一个InvocationHandler对象
InvocationHandler handler=new MyInvokationHandler();
//使用指定的InvocationHanlder来生成一个动态代理对象
PersonPro p=(PersonPro)Proxy.newProxyInstance(PersonPro.class.getClassLoader(), new Class[]{PersonPro.class}, handler);
//调用动态代理对象的walk()方法和sayHello()方法
p.walk();
p.sayHello("luck");
}
}
interface PersonPro{
void walk();
void sayHello(String name);
}
class MyInvokationHandler implements InvocationHandler{
/*
* 执行动态代理对象的所有方法时,都会被替换成执行如下invoke方法
* 其中:
* proxy--代表动态代理对象
* method--代表正在执行的方法
* args--代表执行代理对象方法时传入的实参
*/
public Object invoke(Object proxy,Method method,Object[] args){
System.out.println("正在执行的方法:"+method);
if(args!=null){
System.out.println("下面是执行该方法时传入的实参:");
for(Object val:args){
System.out.println(val);
}
}else{
System.out.println("调用该方法无须实参");
}
return null;
}
}