[Java]反射、String类补充
目录
1、反射定义
2、用途(了解)
3、反射相关的类
4、Class类(反射机制的起源)
4.1、相关方法
5、反射示例
5.1、获取Class对象
5.2、反射的使用
6、反射优点和缺点
7、String类补充
7.1、创建对象的思考
8、字符串常量池
9、再谈String对象创建
10、intern方法
1、反射定义
Java的反射(reflection)机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意方法和属性,既然能拿到那么,我们就可以修改部分类型信息;这种动态获取信息以及动态调用对象方法的功能称为java语言的反射(reflection)机制
反射基本信息:
Java程序中许多对象在运行时会出现两种类型:运行时类型(RTTI)和编译时类型,例如Person p = new Student();这句代码中p在编译时类型为Person,运行时类型为Student。程序需要在运行时发现对象和类的真实 信息。而通过使用反射程序就能判断出该对象和类属于哪些类
2、用途(了解)
1. 在日常的第三方应用开发过程中,经常会遇到某个类的某个成员变量、方法或是属性是私有的或是只对系统应用开放,这时候就可以利用Java的反射机制通过反射来获取所需的私有成员或是方法 。
2. 反射最重要的用途就是开发各种通用框架,比如在spring中,我们将所有的类Bean交给spring容器管理,无 论是XML配置Bean还是注解配置,当我们从容器中获取Bean来依赖注入时,容器会读取配置,而配置中给的 就是类的信息,spring根据这些信息,需要创建那些Bean,spring就动态的创建这些类。
3、反射相关的类
4、Class类(反射机制的起源)
Class类:代表类的实体,在运行的Java应用程序中表示类和接口
Java文件被编译后,生成了.class文件,JVM此时就要去解读.class文件 ,被编译后的Java文件.class也被JVM解析为一个对象,这个对象就是 java.lang.Class .这样当程序在运行时,每个java文件就最终变成了Class类对象的一个实例。
我们通过Java的反射机制应用到这个实例,就可以去获得甚至去添加改变这个类的属性和动作,使得这个类成为一个动态的类
4.1、相关方法
(重要)常用获得类相关的方法
(重要)常用获得类中属性相关的方法(以下方法返回值为Field)
(了解)获得类中注解相关的方法(以下方法返回值为Constructor)
(重要)获得类中方法相关的方法(以下方法返回值为Method)
5、反射示例
以Student类为例
package demo1;class Student {//私有属性nameprivate String name = "bit";//公有属性agepublic int age = 18;//不带参数的构造方法public Student(){System.out.println("Student()");}private Student(String name,int age) {this.name = name;this.age = age;System.out.println("Student(String,name)");}private void eat(){System.out.println("i am eat");}public void sleep(){System.out.println("i am pig");}private void function(String str) {System.out.println(str);}@Overridepublic String toString() {return "Student{" +"name='" + name + '\'' +", age=" + age +'}';}
}
5.1、获取Class对象
在反射之前,我们需要做的第一步就是先拿到当前需要反射的类的Class对象,然后通过Class对象的核心方法,达到反射的目的,即:在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意方法和属性,既然能拿到那么,我们就可以修改部分类型信息。
(推荐前两种,不推荐第三种)
第一种,使用 Class.forName("类的全路径名"); 静态方法。
前提:已明确类的全路径名。
第二种,使用 .class 方法。
说明:仅适合在编译前就已经明确要操作的 Class
第三种,使用类对象的 getClass() 方法
c1、c2、c3都是同一个对象,得出结论:Class对象只有一个
5.2、反射的使用
依旧反射上面的Student类
注意:所有和反射相关的包都在 import java.lang.reflect 包下面
1. 创建对象
2. 反射私有的构造方法
3. 反射私有属性 (name)
4. 反射私有方法 (function)
6、反射优点和缺点
优点:
1. 对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法
2. 增加程序的灵活性和扩展性,降低耦合性,提高自适应能力
3. 反射已经运用在了很多流行框架如:Struts、Hibernate、Spring 等等。
缺点:
1. 使用反射会有效率问题。会导致程序效率降低。比如说,为了调用一个方法,调用了五个方法,五个方法才相当于一个方法,效率必然不高。具体参考这里:http://www.imooc.com/article/293679
2. 反射技术绕过了源代码的技术,因而会带来维护问题。反射代码比相应的直接代码更复杂
7、String类补充
7.1、创建对象的思考
下面两种创建String对象的方式相同吗?
上述程序创建方式类似,为什么s1和s2引用的是同一个对象,而s3和s4不是呢?
在Java程序中,类似于:1, 2, 3,3.14,“hello”等字面类型的常量经常频繁使用,为了使程序的运行速度更快、 更节省内存,Java为8种基本数据类型和String类都提供了常量池
为了节省存储空间以及程序的运行效率,Java中引入了:
1. Class文件常量池:每个.Java源文件编译后生成.Class文件中会保存当前类中的字面常量以及符号信息
2. 运行时常量池:在.Class文件被加载时,.Class文件中的常量池被加载到内存中称为运行时常量池,运行时常量池每个类都有一份
3. 字符串常量池
8、字符串常量池
字符串常量池在JVM中是StringTable类,实际是一个固定大小的HashTable(哈希表),不同JDK版本下字符串常量池的位置以及默认大小是不同的:
9、再谈String对象创建
String对象创建的底层逻辑:String s3 = new String("world");
只要是双引号引起来的都会存放在常量中
1. 当s1创建完成后,再创建s2,先去常量池查找是否有当前这个字符串,如果有,会把常量池中这个对象的地址给到s2。这样一来,s1和s2都指向了同一个对象,所以打印 s1==s2 的返回值一定为true
2. 创建 s3 = new String("world"),首先在常量池中查找是否有world这个字符串,没有就跟 s1 一样就存放到常量池中;再创建一个新对象 s4 = new String("world"),首先在常量池中找到了world这个字符串,此时会new一个新的String对象,然后把存放world字符串的地址给到新对象的 value
结论:
1. 常量池中有,就用常量池里的;常量池中没有,就把它放到常量池
2. 只要是new的对象,都是唯一的。
通过上面例子可以看出:使用常量串创建String类型对象的效率更高,而且更节省空间。
也可以将创建的字符串对象通过 intern 方式添加进字符串常量池中。
10、intern方法
先分析这段代码
它的底层存储:
1. 创建 s1= new String(ch),参数ch不是双引号引起来的,所以不放到常量池,它的存储方式是:new一个新对象,其中的value指向拷贝后的新数组
2. s2 = "abc",abc是双引号引起来的,所以放到常量池;
3. 综上,s1和s2没有任何关系,所以这段代码的结果为false
intern方法:让s1所指向的对象入池;如果常量池中存在,就不入池
s1入池后,创建s2 = "abc",在常量池找到了这个字符串,最终s1和s2指向的是同一个对象,所以加上intern方法后,代码执行结果为true
public static void main(String[] args) {char[] ch = new char[]{'a', 'b', 'c'};String s1 = new String(ch); // s1对象并不在常量池中//s1.intern(); // s1.intern();调用之后,会将s1对象的引用放入到常量池中String s2 = "abc"; // "abc" 在常量池中存在了,s2创建时直接用常量池中"abc"的引用System.out.println(s1 == s2);
}
// 输出false
// 将上述方法打开之后,就会输出true