文章目录
一、类的对象和类对象
类的对象:基于某个类new出来的对象,也叫做实例对象。
类对象:每一个类加载到内存后都对应有且只有一个class对象,封装了一个类所有的信息,每一个类都是class类的一个对象(利用-verbose:class获得类的加载过程)。
1、获取类对象
1.通过类的对象,获得类对象
Student s = new Student();
Class c = s.getClass();
2.通过类名获取类对象
Class c = 类名.class;
3.通过静态方法,获得类对象,推荐使用这种方式
Class c = Class.forName("包名.类名");
示例:
/*创建一个类*/
public class Student{
private int age=0;
private String name="";
public Student(){
}
}
/*通过三种方式获得类的类对象*/
public class GetClass{
//1.第一种方式
Student stu = new Student();
Class<?> c1 = stu.getClass();
System.out.println(c1.hashCode());
//2.第二种方式
Class c2 = Student.class;
System.out.println(c2.hashCode());
//3.第三种方式
try {
Class c3 = Class.forName("GetClassTest.Student");
System.out.println(c3.hashCode());
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
输出:
366712642
366712642
366712642
2、类对象的常用方法
1. 获取类的名字
public String getName();
2. 获得类所在包的包名
public Package getPackage();
3. 获得父类
public Class<? super T> getSuperclass();
4. 获得接口
public Class<?>[] getInterfaces();
5. 获得构造方法
public Constructor<?>[] getConstructors();
6. 创建该类的实例
public T newInstance();
7. 获得方法(只能获取公开的方法,包括从父类继承的方法)
public Method[] getMethods();
8. 获得声明的方法(包括私有保护的方法,不包括从父类继承的方法)
public Method[] getDeclearedMethods();
9. 获得属性(只能获取公开的属性,包括从父类继承得到的属性)
public Field[] getFields();
10. 获得声明的属性(获取公开的属性,不包括从父类继承得到的属性)
public Field[] getDeclearedFields();
3、获取类的构造方法并创建对象
/*创建一个类*/
public class Student {
private int age;
private String name;
public Student(int age,String name) {
this.age=age;
this.name=name;
System.out.println("调用了带参构造函数");
}
public Student() {
this.age=1;
this.name="abc";
System.out.println("调用了不带参构造函数");
}
@Override
public String toString() {
return "age:"+age+",name:"+name;
}
}
/*通过类对象获取构造函数,实例化一个对象*/
public class GetClass {
public static void main(String[] args) throws Exception {
//1.用"包名.类名"创建类对象
Class<?> c1 = Class.forName("GetClassTest.Student");
//2.获得所有构造方法
Constructor<?>[] cons = c1.getConstructors();
for(Constructor c:cons) {
System.out.println(c.toString());
}
//3.利用不带参构造函数,实例化一个对象
Constructor<?> con1 = c1.getConstructor();
Object obj1 = con1.newInstance();
System.out.println(obj1.toString());
//4.利用带参构造函数,实例化一个对象
Constructor<?> con2 = c1.getConstructor(int.class,String.class);//参数类型和顺序
Object obj2 = con2.newInstance(5,"AAA");
System.out.println(obj2.toString());
}
}
输出:
//1.
public GetClassTest.Student(int,java.lang.String)
public GetClassTest.Student()
//2.
调用了不带参构造函数
age:1,name:abc
//3.
调用了带参构造函数
age:5,name:AAA
4、调用类的其它方法
/*创建一个类*/
public class Student {
private int age;
private String name;
public Student(int age,String name) {
this.age=age;
this.name=name;
System.out.println("调用了带参构造函数");
}
public Student() {
this.age=1;
this.name="abc";
System.out.println("调用了不带参构造函数");
}
@Override
public String toString() {
return "age:"+age+",name:"+name;
}
public void suck(int len) {
System.out.println("带参的方法");
}
private void cum() {
System.out.println("私有的方法");
}
public static void bump() {
System.out.println("静态方法");
}
}
public class GetClass {
public static void main(String[] args) throws Exception {
Class<?> c1 =Class.forName("GetClassTest.Student");
//1.获得
Method[] methods = c1.getMethods();
for(Method method:methods) {
System.out.println(method.toString());
}
//2.获得声明的方法
Method[] methods2 = c1.getDeclaredMethods();
for(Method method:methods2) {
System.out.println(method.toString());
}
//3.带参方法
Method m1 = c1.getMethod("suck",int.class);
Student stu = (Student)c1.newInstance();
m1.invoke(stu,5);
//4.获取私有方法
Method m2 = c1.getDeclaredMethod("cum");
m2.setAccessible(true);//设置为true才能访问私有方法
m2.invoke(stu);
//5.获取静态方法
Method m3 = c1.getMethod("bump");
m3.invoke(null);//调用静态方法不需要通过该类的对象
}
}
输出:
//1.非常多方法,略
//2.
public java.lang.String GetClassTest.Student.toString()
private void GetClassTest.Student.cum()
public static void GetClassTest.Student.bump()
public void GetClassTest.Student.suck(int)
//3.
调用了不带参构造函数
带参的方法
//4.
调用了不带参构造函数
私有的方法
//5.
调用了不带参构造函数
静态方法
5、利用反射实现调用类的方法的通用方法
public class Student {
private int age;
private String name;
public Student(int age,String name) {
this.age=age;
this.name=name;
System.out.println("调用了带参构造函数");
}
public Student() {
this.age=1;
this.name="abc";
System.out.println("调用了不带参构造函数");
}
@Override
public String toString() {
return "age:"+age+",name:"+name;
}
public void suck(int len) {
System.out.println("带参的方法");
}
private void cum() {
System.out.println("私有的方法");
}
public static void bump() {
System.out.println("静态方法");
}
}
public class GetClass {
public static Object invokeAny(Object obj,String methodName,Class<?>[] types,Object...args)throws Exception{
Class<?> c1 = obj.getClass();
Method method = c1.getMethod(methodName,types);
return method.invoke(obj, args);
}
public static void main(String[] args) throws Exception {
Student stu = new Student();
invokeAny(stu,"suck",new Class[] {
int.class},5);
}
}
输出:
调用了不带参构造函数
带参的方法
6、获取类的属性
/*Student类同上*/
public class GetClass {
public static void main(String[] args) throws Exception {
Class c1 = Class.forName("GetClassTest.Student");
//1.获得声明的所有属性
Field[] fields = c1.getDeclaredFields();
for(Field f:fields) {
System.out.println(f.toString());
}
//2.修改属性
Field f1 = c1.getDeclaredField("age");//对应实例的一个属性
f1.setAccessible(true);//因为属性是私有的所以要修改为true
Student stu = (Student)c1.newInstance();//实例化一个对象
f1.set(stu, 16);//设置对象的属性值,同理有get()方法获得属性值
System.out.println(stu.toString());
}
}
输出:
//1.
private int GetClassTest.Student.age
private java.lang.String GetClassTest.Student.name
//2.
调用了不带参构造函数
age:16,name:abc
二、工厂设计模式
设计模式:特定问题的固定解决方法。使用设计模式是为了可重用代码,让代码更容易被理解,保证代码可靠性、重用性。
工厂模式:主要负责对象创建的问题。其中一个重要原则是“开闭原则”,对拓展开放,对修改关闭。
工厂模式下一般分为四个部分:父类、子类、工厂、客户。
示例:
父类:
public interface USB {
public void service();
}
子类1:
public class Mouse implements USB{
@Override
public void service() {
System.out.println("鼠标正在工作");
}
}
子类2:
public class Board implements USB{
@Override
public void service() {
System.out.println("键盘正在工作");
}
}
工厂:
public class USBFactory{
public static USB createUSB(int type){
USB usb = null;
if(type==1){
usb = new Mouse();
}else if(type==2){
usb = new Board();
}
return usb;
}
}
客户:
public class Client{
public static void main(String[] args){
System.out.println("选择1购买鼠标,选择2购买键盘");
Scanner input = new Scanner(System.in);
int choice = input.nextInt();
USB usb = USBFactory.createUSB(choice);
usb.service();
}
}
运行
选择1购买鼠标,选择2购买键盘
输入1,输出:鼠标正在工作
输入2,输出:键盘正在工作
但是上述例子如果要增加子类,则需要修改工厂程序,可以利用反射简化上述过程。需要先创建一个properties文件:
1=Factory.Mouse
2=Factory.Board
修改工厂程序的createUSB方法:
public class USBFactory {
public static USB createUSB(String type) throws Exception{
USB usb = null;
Class<?> c = Class.forName(type);
usb = (USB)c.newInstance();
return usb;
}
}
修改客户程序:
public class Client {
public static void main(String[] args) throws Exception {
System.out.println("选1 购买鼠标,选2购买键盘");
Scanner input = new Scanner(System.in);
String choice = input.next();
Properties pro = new Properties();
FileInputStream fis = new FileInputStream("src/Factory/usb.properties");
pro.load(fis);
fis.close();
USB usb = USBFactory.createUSB(pro.getProperty(choice));
if(usb!=null) {
System.out.println("购买成功");
usb.service();
}
else {
System.out.println("购买失败");
}
}
}
运行结果也是相同的,但需要扩展子类的种类的话只需要创建子类后在properties文件中增加条目,代码重用度提高。
三、单例模式
单例:只允许创建一个该类的对象
方式1:饿汉式单例(类加载时创建,天生线程安全)
创建过程:①、首先创建一个常量;②、构造方法改成私有的,类外部不能创建对象;③、通过一个公开的方法,返回这个对象
优缺点:线程安全,生命周期太长,浪费空间,不使用也会实例化。
/*创建一个类*/
public class SingleTon {
private static final SingleTon instance = new SingleTon();
private SingleTon() {
}//私有构造方法,禁止其他类创建该类的对象
public SingleTon getSingleTon() {
return instance;
}//提供该类的对象的访问
}
/*测试*/
public class SingleTest {
public static void main(String[] args) {
for(int i=0;i<3;i++) {
new Thread(new Runnable() {
@Override
public void run() {
System.out.println(SingleTon.getSingleTon().hashCode());
}
}).start();
}
}
}
输出:
1680256105
1680256105
1680256105
方式2:懒汉式单例(使用时创建,线程不安全,加同步)
创建过程:①、首先创建一个对象,赋值为null;②、构造方法改成私有的,类外部不能创建对象;③、通过一个公开的方法,返回这个对象。
优缺点:生命周期比较短,节省空间,有线程安全,可以通过同步方法和同步代码块解决这个问题。
/*创建一个类*/
public class SingleTon {
private static SingleTon instance = null;
private SingleTon() {
}
/*使用synchronized保证同步*/
public static synchronized SingleTon getSingleTon() {
if(instance==null) {
instance = new SingleTon();
}
return instance;
}
}
/*测试代码不变*/
输出:
1311878651
1311878651
1311878651
方式3:懒汉式单例(使用时创建,线程安全,静态内部类写法)
/*创建一个类*/
public class SingleTon {
private SingleTon() {
}
private static class Holder{
static SingleTon s = new SingleTon();//创建一个静态对象
}
public static SingleTon getSingleTon() {
return Holder.s;
}
}
/*测试代码不变*/
输出:
2003228057
2003228057
2003228057
四、枚举
什么是枚举?枚举是一个引用类型,枚举是一个规定了取值范围的数据类型。枚举变量不能使用其他的数据,只能使用枚举中常量赋值,提高程序安全性,使用的是unum关键字。
①、枚举必须包含枚举常量,可以包含属性、方法、私有构造方法;
②、枚举常量必须写在前面,多个常量之间用逗号隔开,最后分号可写可不写。
③、枚举的本质:枚举是一个终止类(final class),并继承Enum抽象类,枚举中常量是当前类型的静态常量。
例如:
public enum Gender {
MALE,FAMALE;
}
public class GenderTest {
public static void main(String[] args) {
Gender gender = Gender.FAMALE;
System.out.println(gender.toString());
}
}
输出:
FEMALE
上面的Gender.class利用XJad反编译工具反编译后的java文件内容:
public final class Gender extends Enum
{
public static final Gender MALE;
public static final Gender FAMALE;
private static final Gender ENUM$VALUES[];
private Gender(String s, int i)
{
super(s, i);
}
public static Gender[] values()
{
Gender agender[];
int i;
Gender agender1[];
System.arraycopy(agender = ENUM$VALUES, 0, agender1 = new Gender[i = agender.length], 0, i);
return agender1;
}
public static Gender valueOf(String s)
{
return (Gender)Enum.valueOf(enumtest/Gender, s);
}
static
{
MALE = new Gender("MALE", 0);
FAMALE = new Gender("FAMALE", 1);
ENUM$VALUES = (new Gender[] {
MALE, FAMALE
});
}
}
枚举类型经常搭配switch一起使用:
public enum Season {
SPRING,SUMMER,FALL,WINTER;
}
public class SeasonTest {
public static void main(String[] args) {
Season season = Season.WINTER;
switch(season) {
case SPRING:
System.out.println("spring");break;
case SUMMER:
System.out.println("summer");break;
case FALL:
System.out.println("fall");break;
case WINTER:
System.out.println("winter");break;
}
}
}
输出:
winter
五、注解
1、什么是注解?注解是代码里的特殊标记,程序可以读取注解,一般用来代替配置文件。可以通过反射去得到类里的注解,以决定去运行什么类。
定义注解用@interface关键字,注解里面只能包含属性。
注解的属性类型:String类型、基本数据类型、Class类型、枚举类型、注解类型以及上述类型的一维数组。
举个例子:
public @interface MyAnnotation {
//可以设置默认值,属性后面需要加圆括号
String name() default "ABC";
int age() default 123;
}
上述MyAnnotation.class反编译后的内容,继承了Annotation接口,原先的属性变成方法:
import java.lang.annotation.Annotation;
public interface MyAnnotation extends Annotation
{
public abstract String name();
public abstract int age();
}
2、元注解:同来描述注解的注解
@Retention:用于指定注解可以保留的域
① RetentionPolicy.CLASS:注解记录在class文件中,运行java程序时,JVM不会保留,这是默认值。
② RetentionPolicy.RUNTIME:注解记录在class文件中,运行java程序时,JVM会保留注解,程序可以通过反射获取该注释。
③ RetentionPolicy.SOURCE:编译时直接丢弃这种策略的注释。
示例:
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
/*如果没有设置RUNTIME,获取的注解就会变成null*/
@Retention(value=RetentionPolicy.RUNTIME)
public @interface MyAnnotation {
String name();
int age();
String sex();
}
public class Person {
@MyAnnotation(name="abc",age=10,sex="female")
public void show(String name,int age,String sex) {
System.out.println(name+" "+age+" "+sex);
}
}
public class Demo {
public static void main(String[] args) throws Exception {
Class<?> c1 = Class.forName("annotationtest.Person");
Method method = c1.getMethod("show", String.class,int.class,String.class);
MyAnnotation myanno = method.getAnnotation(MyAnnotation.class);//得到方法上的注解
System.out.println(myanno.name());
System.out.println(myanno.age());
System.out.println(myanno.sex());
Person person = (Person)c1.newInstance();
method.invoke(person, myanno.name(),myanno.age(),myanno.sex());
}
}
输出:
abc
10
female
abc 10 female
@Target:指定注解用于修饰类的哪个成员
示例:
/*此时注解只能应用在类的方法上*/
@Retention(value=RetentionPolicy.RUNTIME)
@Target(value= {
ElementType.METHOD})
public @interface MyAnnotation {
String name();
int age();
String sex();
}
关于反射的简单学习到此结束~