近期应该是跳槽的高峰期,好多朋友不是在跳槽就是在跳槽的路上,下面个人想总结与加强一下自己的记忆故根据朋友的面试遇到的问题来解答一下。
1.说说java中的基本数据类型?
Java基本类型共有八种,基本类型可以分为三类,字符类型char,布尔类型boolean以及数值类型byte、short、int、long、float、double。数值类型又可以分为整数类型byte、short、int、long和浮点数类型float、double。
JAVA中的数值类型不存在无符号的,它们的取值范围是固定的,不会随着机器硬件环境或者操作系统的改变而改变。实际上,JAVA中还存在另外一种基本类型void,它也有对应的包装类 java.lang.Void,不过我们无法直接对它们进行操作。8 中类型表示范围如下:
byte:8位,最大存储数据量是255,存放的数据范围是-128~127之间。
short:16位,最大数据存储量是65536,数据范围是-32768~32767之间。
int:32位,最大数据存储容量是2的32次方减1,数据范围是负的2的31次方到正的2的31次方减1。
long:64位,最大数据存储容量是2的64次方减1,数据范围为负的2的63次方到正的2的63次方减1。
float:32位,数据范围在3.4e-45~1.4e38,直接赋值时必须在数字后加上f或F。
double:64位,数据范围在4.9e-324~1.8e308,赋值时可以加d或D也可以不加。
boolean:只有true和false两个取值。
char:16位,存储Unicode码,用单引号赋值。
Java决定了每种简单类型的大小。这些大小并不随着机器结构的变化而变化。这种大小的不可更改正是Java程序具有很强移植能力的原因之一。下表列出了Java中定义的简单类型、占用二进制位数及对应的封装器类。
拓展:基本数据类型是数据类型的一种,还有一种是引用数据类型,引用数据类型包括:类(class),接口(interface),数组([])
2.创建线程的几种方式?
首先我们要知道创建线程有两种方式:
1.继承Thread类,并重写run()方法
2.实现Runnable接口,覆盖接口中的run()方法,并把Runnable接口的实现扔给Thread
public static void main(String[] args) {
// 第一种
MyThread myThread = new MyThread();
myThread.start();
// 第二种
new Thread(() -> System.out.println("自己实现的run-2")).start();
}
public static class MyThread extends Thread {
@Override
public void run() {
System.out.println("自己实现的run-1");
}
}
如果问还有没有其他方式的时候,Java5 之后的Executors,Executors工具类可以用来创建线程池。
Executors工具类是用来创建线程池的,这个线程池可以指定线程个数,也可以不指定,也可以指定定时器的线程池,它有如下常用的方法:
newFixedThreadPool(int nThreads):创建固定数量的线程池
newCachedThreadPool():创建缓存线程池
newSingleThreadExecutor():创建单个线程
newScheduledThreadPool(int corePoolSize):创建定时器线程池
上面的两种创建线程的方式,都涉及到了run()方法,你了解过Thread里的run()方法具体是怎么实现的吗?
这里就涉及到有没有看过源码了,源码里面Thread 中的run()方法里东西很少,就一个 if 判断:
@Override
public void run() {
if (target != null) {
target.run();
}
}
有个target对象,会去判断该变量是否为空,非空的时候,去执行target对象中的run()方法,否则啥也不干。而这个target对象,就是我们说的Runnable:
/* What will be run. */
private Runnable target;
那这个Runnable类你了解过吗?
了解过,这个Runnable类很简单,就一个抽象方法:
@FunctionalInterface
public interface Runnable {
public abstract void run();
}
这个抽象方法也是run()!如果我们使用Runnable接口,就需要实现这个run()方法。由于这个Runnable类上面标了@FunctionalInterface注解,所以可以使用函数式编程。
所以这就对应了刚才说的两种创建线程的方式,假如我用第一种方式:继承了Thread类,然后重写了run()方法,那么它就不会去执行上面这个默认的run()方法了(即不会去判断target),会执行我重写的run()方法逻辑。
假如我是用的第二种方式:实现Runnable接口的方式,那么它会执行默认的run()方法,然后判断target不为空,再去执行我在Runnable接口中实现的run()方法。
那如果我既继承了Thread类,同时我又实现了Runnable接口,比如这样,最后会打印什么信息出来呢?
public static void main(String[] args) {
new Thread(() -> System.out.println("runnable run")) {
@Override
public void run() {
System.out.println("Thread run");
}
}.start();
}
打印结果是:“Thread run”,但应该很多人不知道为什么,下面来说说原因
其实这个答案很简单,我们来分析一下代码便知:其实是 new 了一个对象(子对象)继承了Thread对象(父对象),在子对象里重写了父类的run()方法;然后父对象里面扔了个Runnable进去,父对象中的run()方法就是最初那个带有 if 判断的run()方法。
好了,现在执行start()后,肯定先在子类中找run()方法,找到了,父类的run()方法自然就被干掉了,所以会打印出:Thread run。
如果我们现在假设子类中没有重写run()方法,那么必然要去父类找run()方法,父类的run()方法中就得判断是否有Runnable传进来,现在有一个,所以执行Runnable中的run()方法,那么就会打印:Runnable run 出来。
3.springboot与spring简单做下比较
既然要比较,那就要先说说各自的意义:
(1)spring:
Spring框架为开发Java应用程序提供了全面的基础架构支持。它包含一些很好的功能,如依赖注入和开箱即用的模块
Spring JDBC
Spring MVC
Spring Security
Spring AOP
Spring ORM
Spring Test
这些模块可以大大缩短应用程序的开发时间。例如,在Java Web开发的早期阶段,我们需要编写大量的重复代码来将记录插入到数据源中。但是通过使用Spring JDBC模块的JDBCTemplate,我们可以将它简化为只需几个简单配置或者几行代码。
(2)springboot:
Spring Boot基本上是Spring框架的扩展,它消除了设置Spring应用程序所需的复杂例行配置。
它的目标和Spring的目标是一致的,为更快,更高效的开发生态系统铺平了道路。以下是Spring Boot中的一些功能:
1.通过starter这一个依赖,以简化构建和复杂的应用程序配置。
2.可以直接main函数启动,嵌入式web服务器,避免了应用程序部署的复杂性,Metrics度量,Helth check健康检查和外部化配置。
3.尽可能的自动化配置Spring功能。
比较起来的话就是springboot在spring的基础上进行了简化配置,依赖于spring但用起来比spring简单很多,原理还是sprin,后期会开个专栏来讲解spring这一块。
上面是面试的部分问题,后期会针对面试继续更新,觉得有帮助的欢迎点赞和关注,谢谢!