1. 反射
1.1 定义
JAVA 反射机制是在运行状态中, 对于任意一个类, 都能够知道这个类的所有属性和方法; 对于任意一个对象, 都能够调用它的任意一个方法.
1.2 作用
在运行时判断任意一个对象所属的类
在运行时构造任意一个类的对象
在运行时判断任意一个类所具有的成员变量和方法 (通过 setAccessible() 方法可访问或修改 private 成员)
在运行时调用任意一个对象的方法
1.3 用法
首先得获取 class 字节码对象, 再通过 class 对象可获取类中的各种属性和方法等
3 种获取 class 对象方法:
1. 通过 Object 类的 getClass 方法
Class clazz = foo.getClass();
2. 通过对象实例方法获取对象
Class clazz = foo.class;
3. 通过 Class.forName 方式
Class clazz = Class.forName("xx.xx.foo");// 完整的类名, 包括所在包
例子:
- public class User {
- private String name;
- private int age;
- private void speak(String name){
- System.out.println("我的名字是:"+name);
- }
- public User(String name,int age)) {
- this.name = name;
- this.age = age;
- }
- }
- User user = new User("张三",25);
- // 获取 User 类中的所有方法
- Method[] methods = User.class.getDeclaredMethods();
- // 获取 User 类中的所有属性
- Field[] fields = User.class.getDeclaredFields();
- // 遍历 User 类的所有属性
- for (int i = 0; i <fields.length; i++) {
- fields[i].setAccessible(true);
- System.out.println(fields[i].getName()+":"+fields[i].get(user));
- }
结果:
name: 张三
- age:25
- (反射基本上可以获取类中所有的信息, 请自行谷歌)
1.4 优缺点
优点:
1. 能够运行时动态获取类的实例, 大大提高了系统的灵活性和扩展性;
2. 与 java 动态编译相结合, 可以实现无比强大的功能.
缺点:
1. 使用反射的性能较低;
2. 使用反射来说相对不安全;
3. 破坏了类的封装性, 可以通过反射来获取这个类的属性和私有方法.
2.String,StringBuilder 和 StringBuffer 的区别
存储
String: 字符串常量, 对象一旦创建, 不可更改
StringBuilder 和 StringBuffer: 字符串变量, 对象可更改
执行速度
String<StringBuffer<StringBuilder;
线程安全
String,StringBuilder 是线程不安全的, StringBuffer 是线程安全的.
适用范围
String: 适用于少量的字符串操作
StringBuilder: 适用于单线程下, 大量字符串操作
StringBuffer: 适用多线程下, 大量字符串操作
3. 单例模式
作用
保证在 Java 程序中, 某个类只有一个实例存在.
spring 中的单例模式
spring 生成对象默认是单例的, 可将 scope 属性设置为 prototype 改为多实例
<bean id="hi" class="com.test.Hi" init-method="init" scope="prototype">
适用场景
1. 需要频繁的进行创建和销毁的对象;
2. 创建对象时耗时过多或耗费资源过多, 但又经常用到的对象;
3. 工具类对象;
4. 频繁访问数据库或文件的对象.
4.Java 中 ++ 操作符是线程安全的吗?
不是线程安全的操作. 它涉及到多个指令, 如读取变量值, 增加, 然后存储回内存, 这个过程可能会出现多个线程交差.
5.== 与 equals 的区别
1. 比较基本数据类型 (int,float,double...) 时, 比较的是它们的值是否相等
2. 比较引用类型(比如 String 类, 自定义的 User 类等), 比较的是引用所指向的对象是否相等, 即对象内存地址是否相同
equals
equals 方法是由 Object 类提供的, 可以由子类来进行重写
Object 类默认的实现如下:
- public boolean equals(Object obj) {
- return (this == obj);
- }
默认的实现只有当对象和自身进行比较时才会返回 true, 这个时候和 "==" 是等价
的.
Java 中很多类 (String 类 Date 类 File 类) 等都对 equals 方法进行了重写, 这
里拿常见的 String 类
- public boolean equals(Object anObject) {
- if (this == anObject) {
- return true;
- }
- if (anObject instanceof String) {
- String anotherString = (String)anObject;
- int n = value.length;
- if (n == anotherString.value.length) {
- char v1[] = value;
- char v2[] = anotherString.value;
- int i = 0;
- while (n-- != 0) {
- if (v1[i] != v2[i])
- return false;
- i++;
- }
- return true;
- }
- }
- return false;
- }
这里, 从 String 的重写 equals 方法可以看出, 比较的是 String 的所存放内容是否相等
equals 方法其实是交给开发者重写, 在自定义类里, 你想用它比较什么都可以, 只要你重写它, 所以我们并不能单纯的说它是用来比较什么的
6. 数组在内存中如何分配
读前须知:
栈: 存放对象引用
堆: 存放所有 new 出来的对象和数组
数组可以存放基本数据类型, 可以存放引用类型. 数组的引用存放于栈中, 实际存放的对象在堆中.
可看以下代码及分析:
- // 存放基本数据类型
- int[] arr = new int[3];
- arr[0] = 1;
- arr[1] = 2;
- arr[2] = 3;
- // 存放自定义类型 User
- User[] userArr =new User[3];
- User user1 = new User("张三");
- User user2 = new User("李四");
- user[0] = user1;
- user[1] = user1;
- user[2] = user2;
运行步骤分析:
存放基本数据类型
1. 在栈中创建 arr 引用
2. 在堆中创建长度 3 的 int 数组, 并将其初始化, 赋默认值 0
3. 将 arr 引用指向 int 数组
4. 对 int 数组中每一个 int 值重新赋值
存放引用类型 User
1. 在栈中创建 userArr 引用
2. 在堆中创建长度 3 的 User 数组, 并将其初始化, 赋默认值 null
3. 将 userArr 引用指向 User 数组
4. 在栈中创建 user1,user2 引用
5. 在堆中创建 User("张三"),User("李四")对象
6. 将 user1,user2 分别指向 User("张三"),User("李四")
7. 分别对 User 数组中的每一个 User 进行重新赋值, 此时 user[0]指向 User("张三"),user[2]指向 User("张三"),user[2]指向 User("李四")
ps: 这里所说的指向是引用变量指向对象, 即引用变量保存了对象在堆内存中的存储地址
可看下图, 更好的理解:
图片描述
ps: 数组所存放的基本数据类型可能存放于堆中, 也可能存放于方法区的常量池中, 此处仅以堆中的基本数据类型为例.
来源: http://www.bubuko.com/infodetail-2678492.html