面试题小记

发布时间 2023-08-12 16:58:37作者: LBC_0612

1、谈谈你对java的理解

java首先是一种面向对象的语言,然后面向对象编程有三个特征:封装、继承、多态、泛型;还有在java8的新特性lambda表达式等

其次呢,java支持跨平台性,一次编译处处运行

继承的好处:提高代码的复用性

注意:java中是单继承的,一个类只能继承一个直接父类,如果子类和父类中出现了同名的成员变量的时候,子类会优先访问自己对象中的成员变量,如果此时想要访问父类中的成员变量,我们可以使用super关键字来解决

super代表的是父类对象的引用,this代表的是当前对象的引用

重载:重载发生在同一个类中,在该类中如果存在多个同名方法,但是方法的参数类型,个数,顺序不一样,那么说明该方法被重载了

重写:重写发生在子类继承父类的关系中,父类中的方法被子类继承,方法名,返回值类型,参数完全不一样,但是方法体不一样,那么说明父类中的该方法被子类重写了

2、java中的反射

1)反射机制其实就是指程序在运行的时候能够获取自身的信息。如果知道一个类的名称或者它的一个实例对象,那么就能把这个类的所有方法和变量的信息(方法名、变量名、方法、修饰符、类型、方法参数等等所有信息)

  找出来。如果明确知道这个类里的某个方法名+参数个数类型,还能通过传递参数来运行那个类里的那个方法

2)当然在平时的编程中,反射基本使用不到,但是在编写框架的时候,反射用的就比较多了,比如我们要使用某一个类型进行操作,但是这个类是通过配置文件配置进来的,那么我们就需要先读取配置文件,然后拿到这个类的全类

     名,然后利用反射API来完成相应的操作

3、开发中哪里使用到反射了

反射是在框架中开发时才使用到的,比如使用JDBC连接数据库的时候,使用Class.forName(驱动类名),就是通过反射来加载数据库的驱动类,还有就是Spring框架中的IOC(动态加载管理Bean对象)创建对象以及AOP(动态代理)使用反

射进行底层实现。还有就是mybatis中Mapper接口代理对象的创建,也是通过反射来实现的

4、java中的内部类

1)定义在内部的,就是内部类。可以定义在方法的内部,也可以定义在类的内部

2)根据定义的位置不同可以分为

  成员内部类:

    普通成员内部类:

      (1)定义在成员位置的内部类,就是成员内部类

      (2)定义格式:

        class 内部类类名{

          内部类成员;

        }

      (3)成员内部类的说明:

        内部类可以直接访问外部类的成员,包括私有成员

        外部类访问内部类的成员,必须先创建内部类对象:

                              

        在外部类以外,创建访问内部类对象的格式

          ①外部类类名.内部类类名 内部类对象名称 = new 外部类类名().new 内部类类名();

           

    私有的成员内部类的说明:

      也就是一个成员内部类,在成员内部类的基础上加上了private关键字在外部类以外,不能直接访问外部类中私有的成员内部类,可以在外部类中定义一访问私有成员内部类中的方法,让外界可以调用公有的方法间接的

                      访问外部类中的私有成员内部类      

        

    静态的成员内部类:

      也是一个成员内部类,在成员内部类的前面加上一个static关键字,静态成员内部类是外部类的静态成员,所以可以通过外部类名.内部类名的方式直接访问,而不需要创建外部类对象

      静态内部类中的非静态成员,需要将所在的内部类对象创建出来之后,才能调用

      静态成员内部类的对象创建格式:外部类类名.内部类类名 变量名 = new 外部类类名.内部类类名();  

      

      总结:一个是否需要创建对象,不取决于该类本身是否需要创建,而取决于访问的成员是否为静态的      

  局部内部类: 

    有名字的内部类:

      定义在方法中的内部类,方法中的局部变量,外界都没有办法进行访问到,所以,方法中的内部类,外界也没有办法访问,可以在方法内部,创建局部内部类对象,调用对象的方法

        外界调用局部类所在的方法,间接的创建局部内部类的对象,间接的访问到内部类的方法

      

    匿名内部类:

      没有名字的内部类

      匿名内部类的使用前提:继承一个类,实现一个接口

      格式:

        new 父类类名或接口名(){

          父类方法的重写或者接口内容的实现;

        }

      匿名内部类本质:创建一个类的子类对象,接口的实现类对象

      内部类名称总结:①如果是有名字的内部类,类名[外部类名$内部类名] 

               

              ②如果是没有名字的内部类,通过匿名内部类的顺序描述类名,第一个匿名内部类的名称[外部类名$1],第二个匿名内部类的名称[外部类名$2]                

              

 5、java中几种常用的线程池

我们可以使用Java.util.concurrent(JUC)包下的Executors工具类,来帮我们创建java给我们提供的几种默认的线程池:

① newCacheTreadPool

  创建一个可以缓存的线程池,如果线程池长度超过处理需要,可以灵活回收空闲线程,没回收的话就新建线程

  总结: 线程池的最大核心线程为无限大,当执行第二个任务时第一个任务已经完成,则会复用执行第一个任务的线程;如果第一个线程任务还没有完成则会新建一个线程。

   

② newFixedThread

  创建一个定长的线程池,可控制最大并发数,超出的线程进行队列等待。

  总结:创建指定长度的线程池,任务超出当前线程池执行线程数量则会一直等待,直到运行。  

   

③ newScheduleThreadPool

  可以创建定长的、支持定时任务,周期任务执行。

  总结:以下案例中延迟2秒后开始执行线程池中的所有任务。

   

④ newSingleExecutor

  创建一个单线程池,它只会用唯一的工作线程来执行任务,保证所有任务按照指定顺序(FIFO, LIFO, 优先级)执行。

  

⑤ 自定义线程池

  通过 new ThreadPoolExecutor() 的写法创建线程池,这样写线程数量更灵活,开发中多数用这个类创建线程。

  

  

  

  

  关于线程池中参数的配置:

    对于线程池的配置,不同的业务场景,不同的服务器我们的配置是不一样的,像我之前用的多线程批量导出报表功能,这种任务属于IO密集型的,它本身由于IO阻塞导致程序运行速度比较慢,所以我多配置了一些线程

    由于我们的服务器 是16核的,我们要尽可能充分利用CPU,所以我们的核心线程数配置了64个,最大线程我们配置了128个,另外等待队列我们配置的很少,因为本身IO阻塞就慢,所以我们就尽量不让过多的线程再进

    入阻塞队列中了

         还有,像我之前用到了线程池xxxx,那种情况属于CPU高速运行的情况我们也叫CPU密集型,对于这种情况,所以我们线程不能配置太多,因为上下文切换也会影响CPU的 运行速度,所以我们配置的核心线程数和我们

    的CPU核心数(16)保持一致,最大线程数我们配置了32个,我们队列就会多配置一些,比如64个,也能配置太多,配置太多的话会造成线程阻塞,如果任务处理还是运算不够的话,可以使用负载均衡来多配置几份项目