1.怎么理解时间复杂度和空间复杂度?
时间复杂度指的是执行的次数,空间复杂度指的是临时占用空间的大小
就是创建次数最多的变量别创建了多少次,
2.什么情况下时间复杂度跟空间复杂可以保持一致?
如果算法语句中就有创建对象这个时间复杂度跟空间复杂度是一致性的
3.数组和链表结构简单对比?
数组:相同数据类型的元素按照一定的顺序进行排序
数组的长度是一定添加元素可能超出元素个数删除会浪费空间
数据的查询比较快但是删除添加比较慢应为删除添加会移动数据
链表:是一种物理存储单元上非连续,非顺序的存储结构,数据元素的逻辑顺序是通过链表中的指针链接次序实现的。
链表是通过指针的形式查询添加删除数据的,就好比new 对象只不过链表把指针通过链表的形式也就是指针指向下一个的方式进行存储,想要查询数据的时候需要遍历指针上边的链表里面指针才能查到数据所以在查询的时候是费时间的,但是在添加和删除的时候只需要修改指针的指向就可以了
4,常见的链表都有那些?
1.单链表:通常链表每一个元素都要保存一个指向下一个元素的指针
2.双向链表:每个元素都要保存到下一个和上一个的指针,还要保存一个上一个元素的指针,
3.循环链表:在最后一个元素中下一个元素指针指向首元素
5,链表的特性?
链表动态进行存储分配可适应数据动态增减
6.链表和数组内存分配内存?
链表和数组都是在堆里面分配内存
7,链表和数组怎么应用?
如果查询快 有少量的删除和添加的时候可以使用数组
如果需要大量的添加和删除数据的时候查询又少我们可以使用
链表
8.怎么遍历树?
先序遍历,中序遍历,后续遍历,层序遍历
先序遍历: 先访问根节点 在访问左子树,最后访问右子树
中序遍历:先访问左子树 在根节点 最后右子树
后续遍历:先左子树 再右子树 最后根节点
层序遍历:每一层从左到右访问每一个节点
每一个子树遍历时依然按照此时的遍历顺序可以采用递归实现遍历
9.冒泡排序?
比较相邻的元素。如果第一个比第二个大就交换他们两个:
对每一对相邻元素作同样的工作,从开始第一对到结尾的最后一对,这样在最后的元素应该会是最大的数;
针对所有的元素重复以上的步骤,除了最后一个;
重复步骤1~3,直到排序完成。
如果两个元素相等,不会再交换位置,所以冒泡排序是一种稳定排序算法。
10.快速排序(Quick Sort)
算法描述:
使用分治法来把一个串(list)分为两个子串(sub-lists)。具体算法描述如下:
从数列中挑出一个元素,称为 “基准”(pivot);
重新排序数列,所有元素比基准值小的摆放在基准前面,所有元素比基准值大的摆在基准的后面(相同的数可以到任一边)。在这个分区退出之后,该基准就处于数列的中间位置。这个称为分区(partition)操作;
递归地(recursive)把小于基准值元素的子数列和大于基准值元素的子数列排序。
key值的选取可以有多种形式,例如中间数或者随机数,分别会对算法的复杂度产生不同的影响。
11:二分查找
算法描述:
二分查找也称折半查找,它是一种效率较高的查找方法,要求列表中的元素首先要进行有序排列。
首先,假设表中元素是按升序排列,将表中间位置记录的关键字与查找关键字比较,如果两者相等,则查找成功;
否则利用中间位置记录将表分成前、后两个子表,如果中间位置记录的关键字大于查找关键字,则进一步查找前一子表,否则进一步查找后一子表。
重复以上过程,直到找到满足条件的记录,使查找成功,或直到子表不存在为止,此时查找不成功。
拓展需求:
当一个有序数组中,有多个相同的数值时,如何将所有的数值都查找到。
12你所知道的设计模式有那些?
Java 中一般认为有 23 种设计模式,我们不需要所有的都会,但是其中常用的几种设计模式应该去掌握。下面列出了所有的设计模式。需要掌握的设计模式我单独列出来了,当然能掌握的越多越好。
总体来说设计模式分为三大类:
创建型模式,共5种:工厂方法模式、抽象工厂模式、单例模式、建造者模式、原型模式。
结构型模式,共7种:适配器模式、装饰器模式、代理模式、外观模式、桥接模式、组合模式、享元模式。
行为型模式,共11种:策略模式、模板方法模式、观察者模式、迭代子模式、责任链模式、命令模式、备忘录模式、状态模式、访问者模式、中介者模式、解释器模式。
13.单例模式?
单例模式确保某个类只有一个实例,而且自行实例化并向整个系统提供这个实例。在计算机系统中,线程池、缓存、日志对象、对话框、打印机、显卡的驱动程序对象常被设计成单例。这些应用都或多或少具有资源管理器的功能。每台计算机可以有若干个打印机,但只能有一个Printer Spooler,以避免两个打印作业同时输出到打印机中。每台计算机可以有若干通信端口,系统应当集中管理这些通信端口,以避免一个通信端口同时被两个请求同时调用。总之,选择单例模式就是为了避免不一致状态。
java中实例就是对象,是某个类的一个对象。类只是一个抽象的东西,对象才是实在的东东。所以叫实例
首先单例模式只能有一个实例就说只能有一个对象自己创建自己的实例
14 单例模式的特点?
单例模式只有一个实例
单例必须自己创建自己的唯一实例
单例类必须给所有其它对象提供这一实例?
单例模式保证了全局对象的唯一性比如系统启动读取配置文件就需要单例保证配置的一致性
唯一性 自己创建自己的唯一实例 提供单例类的实例 读取配置文件需要单例保证一致性
15:实现单例模式的方式?
(1)饿汉式(立即加载)
饿汉式单例再类加载初始化时就创建好了一个静态的对象供外部使用除非系统重启这个对象不会改变所以本身是线程安全
Singleton通过将构造方法限定为private避免了类在外部被实例化,在同一个虚拟机范围内,Singleton的唯一实例只能通过getInstance()方法访问。(事实上,通过Java反射机制是能够实例化构造方法为private的类的,会使Java单例实现失效)
饿汉式就是用private修饰new 对象 通过static修饰再类的加载的时候就已经初始化了,初始化就是第一次给定了默认值,就是在没有动他的时候给的值所以他的线程是安全的,为什么饿汉式是线程安全的应为他通过private修饰是一个私有的然后不可能被其他类调用所以具有安全性
1.package com.atguigu.interview.chapter02;
2.
3./**
4. * @author atguigu
5. *
6. * 饿汉式(立即加载)
7. */
8.public class Singleton {
9.
10. /**
11. * 私有构造
12. */
13. private Singleton() {
14. System.out.println("构造函数Singleton1");
15. }
16.
17. /**
18. * 初始值为实例对象
19. */
20. private static Singleton single = new Singleton();
21.
22. /**
23. * 静态工厂方法
24. * @return 单例对象
25. */
26. public static Singleton getInstance() {
27. System.out.println("getInstance");
28. return single;
29. }
30.
31. public static void main(String[] args){
32. System.out.println("初始化");
33. Singleton instance = Singleton.getInstance();
34. }
35.}
(2)懒汉式
该示例虽然用延迟加载方式实现了懒汉式单例,但在多线程环境下会产生多个Singleton对象
1.package com.atguigu.interview.chapter02;
2.
3./**
4. * @author atguigu
5. *
6. * 懒汉式(延迟加载)
7. */
8.public class Singleton2 {
9.
10. /**
11. * 私有构造
12. */
13. private Singleton2() {
14. System.out.println("构造函数Singleton2");
15. }
16.
17. /**
18. * 初始值为null
19. */
20. private static Singleton2 single = null;
21.
22. /**
23. * 静态工厂方法
24. * @return 单例对象
25. */
26. public static Singleton2 getInstance() {
27. if(single == null){
28. System.out.println("getInstance");
29. single = new Singleton2();
30. }
31. return single;
32. }
33.
34. public static void main(String[] args){
35.
36. System.out.println("初始化");
37. Singleton2 instance = Singleton2.getInstance();
38. }
39.}
从代码上课分析出:懒汉式有点懒他并不像饿汉式那样再类的加载的时候就直接初始化,懒汉式就是再类的加载的时候初始化给了他一个空,再对他引用的时候才会被调用这样就构成了再多线程的情况下会出现线程不安全的情况
(3)同步锁(解决线程安全问题)
在方法上加synchronized同步锁或是用同步代码块对类加同步锁,此种方式虽然解决了多个实例对象问题,但是该方式运行效率却很低下,下一个线程想要获取对象,就必须等待上一个线程释放锁之后,才可以继续运行。
1.package com.atguigu.interview.chapter02;
2.
3./**
4. * @author atguigu
5. *
6. * 同步锁(解决线程安全问题)
7. */
8.public class Singleton3 {
9.
10. /**
11. * 私有构造
12. */
13. private Singleton3() {}
14.
15. /**
16. * 初始值为null
17. */
18. Private static Singleton3 single = null;
19.
20. Public synchronized static Singleton3 getInstance() {
21.
22. if(single == null){
23. single = new Singleton3();
24. }
25.
26. }
27. return single;
28. }
29.}
(4)双重检查锁(提高同步锁的效率)
使用双重检查锁进一步做了优化,可以避免整个方法被锁,只对需要锁的代码部分加锁,可以提高执行效率。
1.package com.atguigu.interview.chapter02;
2.
3./**
4. * @author atguigu
5. * 双重检查锁(提高同步锁的效率)
6. */
7.public class Singleton4 {
8.
9. /**
10. * 私有构造
11. */
12. private Singleton4() {}
13.
14. /**
15. * 初始值为null
16. * 加volatile关键字是为了防止 创建对象时的指令重排问题,导致其他线程使用对象时造成空指针问题。
17. */
18. Private volatile static Singleton4 single = null;
19.
20. /**
21. * 双重检查锁
22. * @return 单例对象
23. */
24. public static Singleton4 getInstance() {
25. if (single == null) {
26. synchronized (Singleton4.class) {
27. if (single == null) {
28. single = new Singleton4();
29. }
30. }
31. }
32. return single;
33. }
34.}
之前加锁我们是对整个方法进行加锁,锁的粒度大影响性能
然后对懒汉的优化进行的改进就是我们要对谁进行操作就对谁进行加锁,就再初始化的对象上通过反射机制进行加锁,
然后通过双重检查是不是被初始化了进行了安全加固
(5)静态内部类:
初步了解类的内部有一个类
这种方式引入了一个内部静态类(static class),静态内部类只有在调用时才会加载,它保证了Singleton 实例的延迟初始化,又保证了实例的唯一性。它把singleton 的实例化操作放到一个静态内部类中,在第一次调用getInstance() 方法时,JVM才会去加载InnerObject类,同时初始化singleton 实例,所以能让getInstance() 方法线程安全。
特点是:即能延迟加载,也能保证线程安全。
静态内部类虽然保证了单例在多线程并发下的线程安全性,但是在遇到序列化对象时,默认的方式运行得到的结果
就是多例的。
1.package com.atguigu.interview.chapter02;
2.
3./**
4. * @author atguigu
5. *
6. * 静态内部类(延迟加载,线程安全)
7. */
8.public class Singleton5 {
9.
10. /**
11. * 私有构造
12. */
13. private Singleton5() {}
14.
15. /**
16. * 静态内部类
17. */
18. private static class InnerObject{
19. private static Singleton5 single = new Singleton5();
20. }
21.
22. public static Singleton5 getInstance() {
23. return InnerObject.single;
24. }
25.}
(6)内部枚举类实现(防止反射和反序列化攻击)
事实上,通过Java反射机制是能够实例化构造方法为private的类的。这也就是我们现在需要引入的枚举单例模式。
1.package com.atguigu.interview.chapter02;
2.
3./**
4. * @author atguigu
5. */
6.public class SingletonFactory {
7.
8. /**
9. * 内部枚举类
10. */
11. private enum EnumSingleton{
12. Singleton;
13. private Singleton6 singleton;
14.
15. //枚举类的构造方法在类加载是被实例化
16. private EnumSingleton(){
17. singleton = new Singleton6();
18. }
19. public Singleton6 getInstance(){
20. return singleton;
21. }
22. }
23.
24. public static Singleton6 getInstance() {
25. return EnumSingleton.Singleton.getInstance();
26. }
27.}
28.
29.class Singleton6 {
30. public Singleton6(){}
31.}
16工厂设计模式(Factory)
(1)什么是工厂设计模式?
工厂设计模式,顾名思义,就是用来生产对象的,在java中,万物皆对象,这些对象都需要创建,如果创建的时候直接new该对象,就会对该对象耦合严重,假如我们要更换对象,所有new对象的地方都需要修改一遍,这显然违背了软件设计的开闭原则,如果我们使用工厂来生产对象,我们就只和工厂打交道就可以了,彻底和对象解耦,如果要更换对象,直接在工厂里更换该对象即可,达到了与对象解耦的目的;所以说,工厂模式最大的优点就是:解耦
工厂模式顾名思义就是像工厂一样生产对象的,同时也可以对对象进行管理,一般情况下我们需要new 一个对象这个时候就会产生连锁效应,大面积的地方都调用了new 对象的时候如果类里面进行的修改那么会联系的到很多类调用这个对象都要修改很麻烦 耦合度也很高,不符合开闭原则,所以我们通过工厂的形式去生产一个自己想要的对象以后类有什么修改和变化我们子需要去跟工厂打交道就可以没必要去修改过多的代码
(2)简单工厂
定义:一个工厂方法 依据传入的参数生成对应的产品对象
角色:
1、抽象产品
2、具体产品
3、具体工厂
4、产品使用者
使用说明:
先将产品类抽象出来,比如,苹果和梨都属于水果,抽象出来一个水果类Fruit,苹果和梨就是具体的产品类,然后创建一个水果工厂,分别用来创建苹果和梨。代码如下: