1、
不算是问题,只是看着好玩,加进来
关于Java重载方法的选择问题(结论:如果有多个方法满足需求,选择最合适的重载方法即可)。
Demo:
/**
* @author LuckyCurve
* @date 2020/5/10 21:41
* 对Java重载的测试案例
* 往往只能确定一个相对适合的方法
*/
public class OverLoadPro {
public static void sayHello(Object arg) {
System.out.println("hello Object");
}
public static void sayHello(int arg) {
System.out.println("hello int");
}
public static void sayHello(long arg) {
System.out.println("hello long");
}
public static void sayHello(Character arg) {
System.out.println("hello Character");
}
public static void sayHello(char arg) {
System.out.println("hello char");
}
public static void sayHello(char... arg) {
System.out.println("hello char ...");
}
public static void sayHello(Serializable arg) {
System.out.println("hello Serializable");
}
public static void main(String[] args) {
sayHello('a');
}
}
使用IDEA即可看出来调用的是哪个方法,然后把调用的方法注释掉,看下一个调用的又是哪个方法:
结论:char>int>long>Character>Serializable>Object>char...
《深入理解Java虚拟机:第三版》的418页有详细分析,图一乐就好。
2、
String flag = new String("hello world");
到底会创建多少个对象
网上答案(有可信度):1个或者2个(如果hello world字符串之前出现过,常量池中就已经存在了,就只创建了1个对象)
查看class文件字节码,如下:
(常量池中对象的创建发生在编译过程中,这里不会涉及到,这里是编译好的方法)
发生了如下几件事儿:
为String对象在堆中分配内存,完成置零操作,并使其引用进栈
复制栈顶元素(1中创建的String对象的引用),并重新压入栈
调用String的init函数,对String对象进行赋值(猜测:两次出栈操作,第一次出栈获取String的值,第二次出栈获取String的引用)
当前方法返回void,还有ireturn返回int等等。
没有问题。
==都只是理论模型,《Java虚拟机规范》中并没有规定虚拟机的实现,只要求预期的结果一致即可,但仍能让我们深刻的了解Java虚拟机的运行思想和逻辑==
上面的栈多指的是操作数栈,一般讨论的入栈出栈都指的是操作数栈,如果进出的对象是栈帧的话那就是虚拟机栈了
参考资料:Class伪指令集网站
3、
学到Lambda表达式的时候就感觉到了,会默认创建一个类(有点匿名类的意思了),并把Lambda的方法体注入到类中去,并返回类的实例化对象。
既然Java支持动态类型检查,可以查看下该对象的实际类型:
选用我们最常见的Runnable函数式接口,分别使用Lambda和匿名类的方式来实例化该接口,并输出对象的实际类型
Demo:
public static void main(String[] args) {
Object o = "hello";
System.out.println(o.getClass().getName());
Runnable runnable1 = new Runnable() {
@Override
public void run() {
System.out.println("hello world");
}
};
System.out.println(runnable1.getClass().getName());
Runnable runnable2 = () -> System.out.println("hello world");
System.out.println(runnable2.getClass().getName());
}
输出:
java.lang.String
cn.luckycurve.demo.character3.InternalFunctionInter$1
cn.luckycurve.demo.character3.InternalFunctionInter$$Lambda$14/0x0000000100066840
获取两个Runnable的类加载器的名称,发现都是app
,而Object o的类加载器无法获取,使用时候会报错。
4、
实现将对象注入到容器当中,看上去不起眼的操作卡了半天,最后还是在StackOverFlow上提问才解决的。
常规解决方法是在方法上标注@Bean注解,然后将该对象作为方法的返回值,这样就实现了IoC容器的注入,但是如果返回值被占用了,这种方法就不起效了,也是我遇到的这种情况。
解决方法其实也找的差不多了,只是没有注意到重载的几个方法,整体思路就是操作ApplicationContext接口,将其转换为实现类GenericWebApplicationContext,调用registerBean方法即可。
刚开始一直想使用:
public <T> void registerBean(Class<T> beanClass, Object... constructorArgs);
但是不想单独为了注入一个对象而为该类增加一个构造函数,是非常麻烦的。
后来的方法:
@Autowired
ApplicationContext context;
@Test
void contextLoads() {
Book book = Book.builder().id("222").build();
GenericWebApplicationContext genericWebApplicationContext = (GenericWebApplicationContext) context;
genericWebApplicationContext.registerBean(Book.class, () -> book);
// 验证结果
Book bean = context.getBean(Book.class);
System.out.println(bean);
}
这里调用的方法是:
public final <T> void registerBean(
Class<T> beanClass, Supplier<T> supplier, BeanDefinitionCustomizer... customizers)
刚开始没反应过来,因为Supplier接口是Java8里提供的,用的不是很多,除非是在使用设计模式的时候我会想起来,用于函数的传递,返回一个beanClass对象即可。第三个参数BeanDefinitionCustomizer用于BeanDefinition的定制,如加上lazy-init或者是primary的特性。