LOADING

加载过慢请开启缓存 浏览器默认开启

2024广联达一面(一)

自我介绍

内存泄漏有哪些情况,用什么工具分析

内存泄漏是指对象在不再被使用时仍然占用内容,导致内存无法释放和回收的情况
内存泄漏的情况有:

  1. 长生命周期对象持有短生命周期对象的引用:当一个长生命周期对象持有了一个短生命周期对象的引用,并且没有及时释放这个引用,就会导致内存泄露。比如在一个集合中存储了大量对象,但是没有及时从集合中移除。
  2. 静态集合对象未及时清理:如果在一个类中定义了静态的集合对象,而这个集合对象一直被引用,并且没有进行及时清理,就会导致内存泄露。
  3. 资源未关闭:在使用一些需要手动关闭的资源,比如数据库连接、文件流、网络连接等时,如果没有在适当的时候关闭这些资源,就会导致内存泄露。
  4. 监听器未注销:在使用监听器时,如果没有及时取消注册或注销监听器,就会导致内存泄露。比如在Android开发中,注册了一个广播接收器但是没有及时注销。

工具:

  1. jmap:查看某个Java进程的堆内存使用情况,包括堆内存的使用量、使用的GC算法、堆内存的配置参数等。
  2. jps:查看当前系统中所有Java进程的进程号。
  3. jvisualvm:Java虚拟机监控工具,可以查看Java进程的内存使用情况、线程使用情况、类加载情况、GC情况等。
  4. jstat:查看某个Java进程的内存使用情况,包括新生代的使用情况、老年代的使用情况、元数据区的使用情况、压缩类空间的使用情况等。
  5. JProfiler:Java虚拟机监控工具,可以查看Java进程的内存使用情况、线程使用情况、类加载情况、GC情况等。(体验用过,过期后没有购买正版)
  6. GCViewer:可以查看GC日志,包括GC的次数、GC的时间、GC前后的堆内存使用情况等。

程序错误怎么调试,程序中断怎么调试

打印调试信息:在代码中使用System.out.println()或者日志输出语句打印一些关键变量的值,以便观察程序的执行流程和变量的取值情况。

使用断点:在IDE中设置断点,然后运行程序,当程序执行到断点处时会暂停执行,可以逐行查看代码的执行情况,同时可以查看变量的值。

单步调试:在IDE中使用单步调试功能,可以逐行执行代码,并观察每一步的执行情况和变量的取值情况。

异常捕获:使用try-catch语句捕获异常,并在catch块中打印异常信息或者进行其他处理。

使用调试工具:Java提供了一些调试工具,例如jdb、jstack等,可以用来查看线程状态、堆栈信息等,有助于定位程序错误。

使用日志工具:使用日志工具记录程序的执行情况和错误信息,可以通过查看日志文件来定位错误。

线程同步有哪些解决方法

线程同步是指:一个线程发出某个功能调用时,在没有得到结果之前,该线程会一直等待,直到该功能调用返回结果,才继续执行后续的代码。线程同步的目的是为了保证线程安全,避免多个线程同时访问同一个资源时出现问题。

线程同步的解决方法有:

  1. 同步机制:使用synchronized关键字对代码块或者方法进行同步,保证同一时间只有一个线程可以访问同步代码块或者同步方法。

1-1. 同步代码块:

使用方法为synchronized(对象) { 代码块 },其中对象是一个对象的引用,可以是任意对象,但是必须保证多个线程使用的是同一个对象。三种方式:继承Thread类、实现Runnable接口、实现Callable接口。

// 继承Thread类
class Window extends Thread{
    // 共享变量static修饰
    private static int ticket = 100;
    // 创建一个对象作为同步代码块的锁
    @Override
    public void run(){
        whilie(true){
            synchronized(Window.class){
                if(ticket > 0){
                    System.out.println(Thread.currentThread.getName() + "卖票,票号为:" + ticket);
                    ticket--;
                }else{
                    break;
                }
            }
        }
    }
}
public class synchronizedTest{
    public static void main(String[] args){
        Window w1 = new Window();
        Window w2 = new Window();
        Window w3 = new Window();
        w1.setName("窗口1");
        w2.setName("窗口2");
        w3.setName("窗口3");
        w1.start();
        w2.start();
        w3.start();
    }
}
// 实现Runnable接口
class Window implements Runnable{
    // 共享变量static修饰
    private static int ticket = 100;
    // 创建一个对象作为同步代码块的锁
    @Override
    public void run(){
        whilie(true){
            synchronized(obj){
                if(ticket > 0){
                    System.out.println(Thread.currentThread.getName() + "卖票,票号为:" + ticket);
                    ticket--;
                }else{
                    break;
                }
            }
        }
    }
}

1-2. 同步方法:

如果操作共享的数据代码完整的声明在一个方法中,我们不妨将此方法声明为同步的。三种方式:继承Thread类、实现Runnable接口、实现Callable接口。

// 继承Thread类
class method extends Thread{
    private static int ticket = 100;
    @Override
    public void run(){
        while(true){
            show();
        }
    }
    // 同步方法
    private static synchronized void show(){
        if(ticket > 0){
            System.out.println(Thread.currentThread.getName() + "卖票,票号为:" + ticket);
            ticket--;
        }
    }
    public class synchronizedTest{
        public static void main(String[] args){
            method m1 = new method();
            method m2 = new method();
            method m3 = new method();
            m1.setName("窗口1");
            m2.setName("窗口2");
            m3.setName("窗口3");
            m1.start();
            m2.start();
            m3.start();
        }
    }
}

1-3. 使用Lock锁:

实例化Reentranlock类,try里面调用lock()方法,finally里面调用unlock()方法。

// 继承Thread类
class Window extends Thread{
   private static int ticket = 100;
    // 实例化Reentranlock类
    private static ReentrantLock lock = new ReentrantLock(); 
    @Override
    public void run(){
        while(true){
            try{
                // 调用lock()方法
                lock.lock();
                if(ticket > 0){
                    System.out.println(Thread.currentThread.getName() + "卖票,票号为:" + ticket);
                    ticket--;
                }else{
                    break;
                }
            }finally{
                // 调用unlock()方法
                lock.unlock();
            }
        }
    }
}
public class lockThread{
    public static void main(){
        Window w1 = new Window();
        Window w2 = new Window();
        Window w3 = new Window();
        w1.setName("窗口1");
        w2.setName("窗口2");
        w3.setName("窗口3");
        w1.start();
        w2.start();
        w3.start();
    }
}

区别1:Synchronized 是Java的一个关键字,而Lock是java.util.concurrent.Locks 包下的一个接口;
区别2:Synchronized 使用过后,会自动释放锁,而Lock需要手动上锁、手动释放锁。(在 finally 块中)
区别3:Lock提供了更多的实现方法,而且 可响应中断、可定时, 而synchronized 关键字不能响应中断;
区别4:synchronized关键字是非公平锁,即,不能保证等待锁的那些线程们的顺序,而Lock的子类ReentrantLock默认是非公平锁,但是可通过一个布尔参数的构造方法实例化出一个公平锁;
区别5:synchronized无法判断,是否已经获取到锁,而Lock通过tryLock()方法可以判断,是否已获取到锁;
区别6:Lock可以通过分别定义读写锁提高多个线程读操作的效率。
区别7:二者的底层实现不一样:synchronized是同步阻塞,采用的是悲观并发策略;Lock是同步非阻塞,采用的是乐观并发策略(底层基于volatile关键字和CAS算法实现)

程序加载时间怎么优化

  1. 减少类加载器的数量,尽量使用系统类加载器,避免自定义类加载器。
  2. 使用缓存:Java中的类加载器可以缓存已经加载的类,避免重复加载同一个类。
  3. 使用JIT编译器:JIT编译器可以将Java字节码编译成本地机器码,提高程序的执行效率。
  4. 避免过度使用动态代理:动态代理会在运行时动态生成代理类,会导致类加载器频繁加载类。
  5. 优化类路径和减少类文件大小:减少类路径中的jar包数量,可以减少类加载器的搜索时间;减少类文件的大小,可以减少类加载器加载类文件的时间。

接口和抽象类的区别

  1. 接口中的方法默认是public abstract的,接口中的变量默认是public static final的,而抽象类中的方法默认是default的,抽象类中的变量默认是default的。
  2. 一个类可以实现多个接口,但只能继承一个抽象类
  3. 定义的关键字不一样,一个是interface,一个是abstract
  4. 属性访问控制符不同,接口中的属性只能是public static final的,而抽象类中的属性没有这个限制
  5. 接口中不能有静态代码块和构造方法,而抽象类中可以有
  6. 方法实现不同:抽象类中的普通方法必须有实现,抽象方法必须在子类中实现,而接口中的普通方法不能有实现。

有哪些集合,linkedlist和arraylist的区别

有两个接口,Collection和Map,Collection接口的实现类有List、Set和Queue,Map接口的实现类有HashMap、TreeMap、LinkedHashMap、HashTable等

两者的区别:
底层数据结构不同:ArrayList底层使用数组实现,LinkedList底层使用双向链表实现。
插入和删除操作:ArrayList插入和删除元素时,需要移动元素,时间复杂度为O(n),而LinkedList插入和删除元素时,不需要移动元素,时间复杂度为O(1)。
随机访问:ArrayList可以通过下标随机访问元素,时间复杂度为O(1),而LinkedList不能通过下标随机访问元素,需要遍历链表,时间复杂度为O(n)。
空间占用:ArrayList的空间占用比LinkedList大,因为ArrayList底层使用数组实现,而数组的长度是固定的,而LinkedList底层使用链表实现,链表的长度是不固定的。
线程安全:ArrayList是线程不安全的,而LinkedList也是线程不安全的。

hashmap底层实现

常规八股

线程池的优点

  1. 降低资源消耗:通过重复利用已创建的线程降低线程创建和销毁造成的消耗。
  2. 提高响应速度:当任务到达时,任务可以不需要等到线程创建就能立即执行。
  3. 提高线程的可管理性:线程是稀缺资源,如果无限制的创建,不仅会消耗系统资源,还会降低系统的稳定性,使用线程池可以进行统一的分配、调优和监控。
    创建方式:使用ThreadPoolExecutor类创建线程池或者使用Executors类创建线程池。

线程的生命周期

  1. 新建状态:使用new关键字创建一个线程,此时线程处于新建状态。
  2. 就绪状态:当调用线程的start()方法时,线程处于就绪状态,此时线程已经被创建,但是还没有被分配到CPU上运行。
  3. 运行状态:当线程被分配到CPU上运行时,线程处于运行状态。
  4. 阻塞状态:当线程执行了sleep()方法、wait()方法、join()方法、suspend()方法等方法时,线程处于阻塞状态。
  5. 销毁状态:当线程执行完了run()方法或者stop()方法时,线程处于销毁状态。

应用层和传输层的协议有哪些

应用层:远程登陆协议Telnet,文本传输协议FTP,超文本传输协议HTTP,域名服务协议DNS,简单邮件传输协议SMTP,邮件协议POP3

传输层协议:传输控制协议TCP,用户数据报协议UDP

事务的特点

  1. 原子性:事务是一个原子操作,由一系列动作组成,事务的原子性确保动作要么全部完成,要么完全不起作用。
  2. 隔离性:事务的隔离性确保事务在并发环境下不会被其他事务干扰,事务执行的中间结果对其他事务必须是透明的。
  3. 一致性:事务执行前后,数据库的完整性约束没有被破坏。
  4. 持久性:事务完成后,事务对数据库的所有更新将被保存到数据库,不能回滚。

什么是脏读

脏读是指在数据库中,事务A读取了事务B未提交的数据,如果事务B回滚,那么事务A读取的数据就是脏数据。
解决方法:使用事务隔离级别,将事务的隔离级别设置为SERIALIZABLE,可以避免脏读。

数据库引擎有哪些

InnoDB和MyISAM
两者的区别:

  1. InnoDB支持事务,MyISAM不支持;
  2. InnoDB支持外键,而MyISAM不支持;
  3. InnoDB是聚集索引,而MyISAM是非聚集索引;
  4. Innodb不支持全文索引,而MyISAM支持全文索引;
  5. InnoDB支持表、行级锁,而MyISAM支持表级锁;
  6. InnoDB表必须有唯一索引,而Myisam可以没有;
  7. 存储文件不同。

了解ThreadLocal吗

ThreadLocal是一个线程内部的数据存储类,可以在指定线程内存储数据,数据存储以后,只有指定线程可以获取到存储的数据,其他线程无法获取到数据。ThreadLocal的作用是提供线程内的局部变量,这种变量在线程的生命周期内起作用,减少同一个线程内多个函数或者组件之间一些公共变量的传递的复杂度。

了解代理吗,动态代理和静态代理的区别

代理是一种常见的设计模式,它允许一个对象在不改变其原有代码的情况下,通过引入一个代理对象来控制对其的访问。代理对象可以在目标对象的前后进行一些额外的操作,例如权限控制、日志记录等。

静态代理是在编译时就已经确定代理关系的代理模式。在静态代理中,代理类和目标类都需要实现同一个接口或者继承同一个父类。代理类持有目标类的引用,并在调用目标方法前后进行一些额外的操作。

动态代理是在运行时动态生成代理类的代理模式。动态代理不需要实现接口或者继承父类,而是通过Java的反射机制,在运行时动态生成代理类。动态代理可以在运行时决定代理类的行为,可以更加灵活地控制目标对象的访问。

静态代理的优点是简单易懂,缺点是如果有多个目标类需要代理,就需要为每个目标类编写一个代理类,导致代码冗余。而动态代理的优点是可以代理多个目标类,且代理逻辑可以在运行时动态确定,缺点是相对于静态代理,动态代理的实现较为复杂。

总结来说,静态代理在编译时确定代理关系,代理类和目标类需要实现相同的接口或者继承相同的父类;动态代理在运行时动态生成代理类,不需要实现相同的接口或者继承相同的父类,更加灵活。

介绍一下数组和链表,各种操作的时间复杂度,应用场景,存储方式

数组:数组是一种线性表数据结构,它用一组连续的内存空间,来存储一组具有相同类型的数据。数组的特点是支持随机访问,根据下标随机访问的时间复杂度为O(1),但是插入和删除元素的时间复杂度为O(n)。
链表:链表是一种线性表数据结构,它用一组任意的内存空间,来存储一组具有相同类型的数据。链表的特点是不支持随机访问,插入和删除元素的时间复杂度为O(1),但是根据下标随机访问的时间复杂度为O(n)。
应用场景:如果需要随机访问元素,那么使用数组;如果需要频繁插入和删除元素,那么使用链表。

介绍一下栈和队列,各种操作的时间复杂度,应用场景,存储方式

栈:栈是一种先进后出的数据结构,它只允许在栈顶进行插入和删除操作,栈的插入和删除操作的时间复杂度为O(1)。
队列:队列是一种先进先出的数据结构,它只允许在队尾进行插入操作,在队头进行删除操作,队列的插入和删除操作的时间复杂度为O(1)。
应用场景:如果需要先进先出的数据结构,那么使用队列;如果需要先进后出的数据结构,那么使用栈。
栈的实现:可以使用数组实现栈,也可以使用链表实现栈。
队列的实现:可以使用数组实现队列,也可以使用链表实现队列。

面向对象的特点

  1. 封装:将数据和对数据的操作封装在一起,对外提供公共的访问方式,隐藏内部的具体实现。
  2. 继承:子类继承父类的属性和方法,子类可以重写父类的方法,实现代码的复用。
  3. 多态:同一个方法可以根据参数的不同,实现不同的功能。

详细阐述一下多态的相关知识

多态是指同一个方法可以根据参数的不同,实现不同的功能。多态有两种实现方式,分别是静态多态和动态多态。
静态多态:静态多态是指方法重载,同一个类中可以定义多个方法,方法名相同,参数列表不同,实现不同的功能。
动态多态:动态多态是指方法重写,子类继承父类,重写父类的方法,实现不同的功能。

设计模式拷打项目中用到了哪些设计模式(45分钟的深挖)

本文作者:GWB
当前时间:2023-11-09 11:11:06
版权声明:本文由gwb原创,本博客所有文章除特别声明外,均采用 CC BY-NC-ND 4.0 国际许可协议。
转载请注明出处!