重载发生在同一个类中,方法名相同、参数列表、返回类型、权限修饰符可以不同
重写发生在子类中,方法名相同、参数列表、返回类型都相同,权限修饰符要大于父类方法,声明异常范围要小于父类方法,但是final和private修饰的方法不可重写
==比较基本类型,比较的是值,
==比较引用类型,比较的是内存地址
equlas是Object类的方法,本质上与== 一样,但是有些类重写了equals方法,比如String的equals被重写后,比较的是字符值,另外重写了equlas后,也必须重写hashcode()方法
1)对于==,比较的是值是否相等
如果作用于基本数据类型的变量,则直接比较其存储的 值是否相等,
如果作用于引用类型的变量,则比较的是所指向的对象的地址是否相等。
其实==比较的不管是基本数据类型,还是引用数据类型的变量,比较的都是值,只是引用类型变量存的值是对象的地址
2)对于equals方法,比较的是是否是同一个对象
首先,equals()方法不能作用于基本数据类型的变量,
另外,equals()方法存在于Object类中,而Object类是所有类的直接或间接父类,所以说所有类中的equals()方法都继承自Object类,在没有重写equals()方法的类中,调用equals()方法其实和使用==的效果一样,也是比较的是引用类型的变量所指向的对象的地址,不过,Java提供的类中,有些类都重写了equals()方法,重写后的equals()方法一般都是比较两个对象的值,比如String类。
ArratList的底层使用动态数组,默认容量为10,当元素数量到达容量时,生成一个新的数组,大小为前一次的1.5倍,然后将原来的数组copy过来;因为数组在内存中是连续的地址,所以ArrayList查找数据更快,由于 扩容机制,添加数据效率更低
LinkedList的底层使用链表,在内存中是离散的,没有扩容机制;LinkedList在查找数据时需要从头遍历,所以查找慢,但是添加数据效率更高
String 由 char[] 数组构成,使用了 final 修饰,对 String 进行改变时每次都会新生成一个 String 对象,然后把指针指向新的引用对象。
StringBuffer可变并且线程安全
StringBuiler可变但线程不安全。
操作少量字符数据用 String;单线程操作大量数据用 StringBuilder;多线程操作大量数据用 StringBuffer。
多态的实现要有继承、重写,父类引用指向子类对象。它的好处是可以消除类型之间的耦合关系,增加类的可扩充性和灵活性。
浅拷贝:浅拷贝只复制某个对象的引用,而不复制对象本身,新旧对象还是共享同一块内存
深拷贝:深拷贝会创造一个一摸一样的对象,新对象和原对象不共享内存,修改新对象不会改变原对对象。
面向对象有封装、继承、多态性的特性,所以相比面向过程易维护、易复用、易扩展,但是因为类调用时要实例化,所以开销大、性能比面向过程低
什么是封装呢?
通过访问修饰符(如 private)来修饰成员变量和成员方法,将不需要对外提供的内容都隐藏起来,提供公共方法对其访问。
封装的好处是:
隐藏实现细节,提供公共的访问方式
提高了代码的复用性
提高安全性
封装的意义在于,将内部的实现细节隐藏起来,对外部的调用者来说是透明的,调用者也不用关心它内部是怎么实现的,只需要知道这个方法是干什么的就好。
什么是继承呢?
在 Java 中子类使用关键词 extend 去继承父类的关系。
继承主要用途是将子类存在共性的东西,把它抽取出来放到父类里面,比如将共同拥有的属性和方法抽取出来放到父类里面。
继承的好处:
想要使用这些属性和方法的时候,可以直接去使用父类的,而不需要自己再重新去定义,更大程度的实现代码复用。
我们不需要写很多的冗余的代码,把共性的全部抽到父类,可以直接调用,如果需要个性化自定义子类的方法时,去重写父类的方法即可。
什么是多态呢?
多态是同一个行为具有多个不同表现形式或形态的能力。
多态就是同一个接口,使用不同的实例而执行不同操作
反射是通过获取类的class对象,然后动态的获取到这个类的内部结构,动态的去操作类的属性和方法。
应用场景有:要操作权限不够的类属性和方法时、实现自定义注解时、动态加载第三方jar包时、按需加载类,节省编译和初始化时间;
获取class对象的方法有:class.forName(类路径),类.class(),对象的getClass()
(1)new关键字 (2)Class.newInstance (3)Constructor.newInstance
(4)Clone方法 (5)反序列化
得分点 :地址空间、开销、并发性、内存
标准回答 :进程和线程的主要差别在于它们是不同的操作系统资源管理方式。
解题思路
得分点: 线程和进程的关系、为什么使用多线程
标准回答 :线程是操作系统调度的最小单元,它可以让一个进程并发地处理多个任务,也叫轻量级进程。所以,在一个进程里可以创建多个线程,这些线程都拥有各自的计数器、堆栈、局部变量,并且能够共享进程内的资源。由于共享资源,处理器便可以在这些线程之间快速切换,从而让使用者感觉这些线程在同时执行。 总的来说,操作系统可以同时执行多个任务,每个任务就是一个进程。进程可以同时执行多个任务,每个任务就是一个线程。一个程序运行之后至少有一个进程,而一个进程可以包含多个线程,但至少要包含一个线程。 使用多线程会给开发人员带来显著的好处,而使用多线程的原因主要有以下几点:
1. 更多的CPU核心 。现代计算机处理器性能的提升方式,已经从追求更高的主频向追求更多的核心发展,所以处理器的核心数量会越来越多,充分地利用处理器的核心则会显著地提高程序的性能。而程序使用多线程技术,就可以将计算逻辑分配到多个处理器核心上,显著减少程序的处理时间,从而随着更多处理器核心的加入而变得更有效率。
2. 更快的响应时间 。我们经常要针对复杂的业务编写出复杂的代码,如果使用多线程技术,就可以将数据一致性不强的操作派发给其他线程处理(也可以是消息队列),如上传图片、发送邮件、生成订单等。这样响应用户请求的线程就能够尽快地完成处理,大大地缩短了响应时间,从而提升了用户体验。
3. 更好的编程模型。 Java为多线程编程提供了良好且一致的编程模型,使开发人员能够更加专注于问题的解决,开发者只需为此问题建立合适的业务模型,而无需绞尽脑汁地考虑如何实现多线程。一旦开发人员建立好了业务模型,稍作修改就可以将其方便地映射到Java提供的多线程编程模型上。
原子性: 一次或多次操作在执行期间不被其他线程影响
可见性: 当一个线程在工作内存修改了变量,其他线程能立刻知道
有序性: JVM对指令的优化会让指令执行顺序改变,有序性是禁止指令重排
解题思路
得分点 :原子类、volatile、锁
标准回答 :Java保证线程安全的方式有很多,其中较为常用的有三种,按照资源占用情况由轻到重排列,这三种保证线程安全的方式分别是原子类、volatile、锁。 JDK从1.5开始提供了java.util.concurrent.atomic包,这个包中的原子操作类提供了一种用法简单、性能高效、线程安全地更新一个变量的方式。在atomic包里一共提供了17个类,按功能可以归纳为4种类型的原子更新方式,分别是原子更新基本类型、原子更新引用类型、原子更新属性、原子更新数组。无论原子更新哪种类型,都要遵循“比较和替换”规则,即比较要更新的值是否等于期望值,如果是则更新,如果不是则失败。 volatile是轻量级的synchronized,它在多处理器开发中保证了共享变量的“可见性”,从而可以保证单个变量读写时的线程安全。可见性问题是由处理器核心的缓存导致的,每个核心均有各自的缓存,而这些缓存均要与内存进行同步。volatile具有如下的内存语义:当写一个volatile变量时,该线程本地内存中的共享变量的值会被立刻刷新到主内存;当读一个volatile变量时,该线程本地内存会被置为无效,迫使线程直接从主内存中读取共享变量。 原子类和volatile只能保证单个共享变量的线程安全,锁则可以保证临界区内的多个共享变量的线程安全,Java中加锁的方式有两种,分别是synchronized关键字和Lock接口。synchronized是比较早期的API,在设计之初没有考虑到超时机制、非阻塞形式,以及多个条件变量。若想通过升级的方式让它支持这些相对复杂的功能,则需要大改它的语法结构,不利于兼容旧代码。因此,JDK的开发团队在1.5新增了Lock接口,并通过Lock支持了上述的功能,即:支持响应中断、支持超时机制、支持以非阻塞的方式获取锁、支持多个条件变量(阻塞队列)。
加分回答 实现线程安全的方式有很多,除了上述三种方式之外,还有如下几种方式:
1. 无状态设计 线程安全问题是由多线程并发修改共享变量引起的,如果在并发环境中没有设计共享变量,则自然就不会出现线程安全问题了。这种代码实现可以称作“无状态实现”,所谓状态就是指共享变量。
2. 不可变设计 如果在并发环境中不得不设计共享变量,则应该优先考虑共享变量是否为只读的,如果是只读场景就可以将共享变量设计为不可变的,这样自然也不会出现线程安全问题了。具体来说,就是在变量前加final修饰符,使其不可被修改,如果变量是引用类型,则将其设计为不可变类型(参考String类)。
3. 并发工具 java.util.concurrent包提供了几个有用的并发工具类,一样可以保证线程安全: - Semaphore:就是信号量,可以控制同时访问特定资源的线程数量。 - CountDownLatch:允许一个或多个线程等待其他线程完成操作。 - CyclicBarrier:让一组线程到达一个屏障时被阻塞,直到最后一个线程到达屏障时,屏障才会打开,所有被屏障拦截的线程才会继续运行。
4. 本地存储 我们也可以考虑使用ThreadLocal存储变量,ThreadLocal可以很方便地为每一个线程单独存一份数据,也就是将需要并发访问的资源复制成多份。这样一来,就可以避免多线程访问共享变量了,它们访问的是自己独占的资源,它从根本上隔离了多个线程之间的数据共享。
解题思路
得分点 :争夺共享资源、相互等待、互斥条件、请求和保持条件、不剥夺条件、环路等待条件
标准回答 :
1. 死锁: 两个或两个以上的进程在执行过程中,因争夺共享资源而造成的一种互相等待的现象,若无外力作用,它们都将无法推进下去。此时称系统处于死锁状态或系统产生了死锁。这些永远在互相等待的进程称为死锁进程。
2. 产生死锁的必要条件: 虽然进程在运行过程中,可能发生死锁,但死锁的发生也必须具备一定的条件,死锁的发生必须具备以下四个必要条件:
- 互斥条件:指进程对所分配到的资源进行排它性使用,即在一段时间内某资源只由一个进程占用。如果此时还有其它进程请求资源,则请求者只能等待,直至占有资源的进程用毕释放;
*- 请求和保持条件:*指进程已经保持至少一个资源,但又提出了新的资源请求,而该资源已被其它进程占有,此时请求进程阻塞,但又对自己已获得的其它资源保持不放;
*- 不剥夺条件:*指进程已获得的资源,在未使用完之前,不能被剥夺,只能在使用完时由自己释放;
- 环路等待条件:指在发生死锁时,必然存在一个进程——资源的环形链,即进程集合 {P0,P1,P2,···,Pn} 中的 P0 正在等待一个 P1 占用的资源;P1 正在等待 P2 占用的资源,……,Pn 正在等待已被 P0 占用的资源。
解题思路
得分点 :管道、命名管道、信号、消息队列、共享内存、内存映射、信号量、Socket
标准答案: 进程间通信主要包括:管道、命名管道、信号、消息队列、共享内存、内存映射、信号量、Socket:
1. 管道 管道也叫无名(匿名)管道,它是是 UNIX 系统 IPC(进程间通信)的最古老形式,所有的 UNIX 系统都支持这种通信机制。管道本质其实是内核中维护的一块内存缓冲区,Linux 系统中通过 pipe() 函数创建管道,会生成两个文件描述符,分别对应管道的读端和写端。无名管道只能用于具有亲缘关系的进程间的通信。 2. 命名管道 匿名管道,由于没有名字,只能用于亲缘关系的进程间通信。为了克服这个缺点,提出了有名管道(FIFO),也叫命名管道、FIFO文件。有名管道(FIFO)不同于匿名管道之处在于它提供了一个路径名与之关联,以 FIFO 的文件形式存在于文件系统中,并且其打开方式与打开一个普通文件是一样的,这样即使与 FIFO 的创建进程不存在亲缘关系的进程,只要可以访问该路径,就能够彼此通过 FIFO 相互通信,因此,通过 FIFO 不相关的进程也能交换数据。
3. 信号 信号是 Linux 进程间通信的最古老的方式之一,是事件发生时对进程的通知机制,有时也称之为软件中断,它是在软件层次上对中断机制的一种模拟,是一种异步通信的方式。信号可以导致一个正在运行的进程被另一个正在运行的异步进程中断,转而处理某一个突发事件。
4. 消息队列 消息队列就是一个消息的链表,可以把消息看作一个记录,具有特定的格式以及特定的优先级,对消息队列有写权限的进程可以向消息队列中按照一定的规则添加新消息,对消息队列有读权限的进程则可以从消息队列中读走消息,消息队列是随内核持续的。
5. 共享内存 共享内存允许两个或者多个进程共享物理内存的同一块区域(通常被称为段)。由于一个共享内存段会称为一个进程用户空间的一部分,因此这种 IPC 机制无需内核介入。所有需要做的就是让一个进程将数据复制进共享内存中,并且这部分数据会对其他所有共享同一个段的进程可用。与管道等要求发送进程将数据从用户空间的缓冲区复制进内核内存和接收进程将数据从内核内存复制进用户空间的缓冲区的做法相比,这种 IPC 技术的速度更快。
6. 内存映射 内存映射(Memory-mapped I/O)是将磁盘文件的数据映射到内存,用户通过修改内存就能修改磁盘文件。
7. 信号量 信号量主要用来解决进程和线程间并发执行时的同步问题,进程同步是并发进程为了完成共同任务采用某个条件来协调它们的活动。对信号量的操作分为 P 操作和 V 操作,P 操作是将信号量的值减 1,V 操作是将信号量的值加 1。当信号量的值小于等于 0 之后,再进行 P 操作时,当前进程或线程会被阻塞,直到另一个进程或线程执行了 V 操作将信号量的值增加到大于 0 之时。
8. Socket 套接字(Socket),就是对网络中不同主机上的应用进程之间进行双向通信的端点的抽象。一个套接字就是网络上进程通信的一端,提供了应用层进程利用网络协议交换数据的机制。Socket 一般用于网络中不同主机上的进程之间的通信。
(1)继承 Tread 类
(2)实现 Runnable 接口
(3)实现 Callable 接口:带有返回值
(4)线程池创建线程
堆内存溢出:(1)当对象一直创建而不被回收时
(2)加载的类越来越多时
(3)虚拟机栈的线程越来越多时
栈溢出:方法调用次数过多,一般是递归不当造成
GC的目的实现内存的自动释放,使用可达性分析法判断对象是否可回收,采用了分代回收思想,将堆分为新生代、老年代,新生代中采用复制算法,老年代采用整理算法,当新生代内存不足时会发生minorGC,老年代不足时会发送fullGC。
原子性:一个事务内的操作统一成功或失败
一致性:事务前后的数据总量不变
隔离性:事务与事务之间相互不影响
持久性:事务一旦提交发生的改变不可逆
事务隔离级别是为了解决脏读、不可重复读、幻读
脏读:一个事务读取了另一个事务未提交的数据
不可重复读:事务A两次读取的数据不一致,读第二次之前可能有其他事务修改了这个数据并提交了
幻读:事务A两次读取数据库,两次查询结果的条数不同,称为幻读。(行数变了即为幻读,数据变了即为不可重复度)
事务隔离级别如下:
读未提交:以上三个问题都解决不了
读已提交:只能解决脏读
可重复读:mysql的默认隔离级别,能解决脏读和不可重复读,包含了间隙锁,可以防止幻读
串行化:都可以解决。(为每个读取操作加一个共享锁)
主键索引:一张表只能有一个主键索引,主键索引列不能有空值和重复值
唯一索引:唯一索引不能有相同值,但允许为空
普通索引:允许出现重复值
组合索引:对多个字段建立一个联合索引,减少索引开销,遵循最左匹配原则
全文索引:myisam引擎支持,通过建立倒排索引提升检索效率,广泛用于搜索引擎
得分点 :检索效率、存储资源、索引维护
标准回答 :索引就像指向表行的指针,是一种允许查询操作快速确定哪些行符合WHERE子句中的条件,并检索到这些行的其他列值的数据结构; 索引主要有普通索引、唯一索引、主键索引、外键索引、全文索引、复合索引几种; 在大数据量的查询中,合理使用索引的优点非常明显,不仅能大幅提高匹配where条件的检索效率,还能用于排序和分组操作的加速。 当时索引如果使用不当也有比较大的坏处:比如**索引必定会增加存储资源的消耗;同时也增大了插入、更新和删除操作的维护成本,**因为每个增删改操作后相应列的索引都必须被更新。
加分回答 :只要创建了索引,就一定会走索引吗? 不一定。 比如,在使用组合索引的时候,如果没有遵从“最左前缀”的原则进行搜索,则索引是不起作用的。 举例,假设在id、name、age字段上已经成功建立了一个名为MultiIdx的组合索引。索引行中按id、name、age的顺序存放,索引可以搜索id、(id,name)、(id, name, age)字段组合。如果列不构成索引最左面的前缀,那么MySQL不能使用局部索引,如(age)或者(name,age)组合则不能使用该索引查询。
聚簇索引:聚簇索引的叶子节点存放的是主键值和数据行;辅助索引(在聚簇索引上创建的其它索引)的叶子节点存放的是主键值或指向数据行的指针。
优点:根据索引可以直接获取值,所以他获取数据更快;对于主键的排序查找和范围查找效率更高;
缺点:如果主键值很大的话,辅助索引也会变得很大;如果用uuid作为主键,数据存储会很稀疏;修改主键或乱序插入会让数据行移动导致页分裂;所以一般我们定义主键时尽量让主键值小,并且定义为自增和不可修改。
非聚簇索引(辅助索引):叶子节点存放的是数据行地址,先根据索引找到数据地址,再根据地址去找数据
他们都是b+数结构
基于粒度:
*表级锁:对整张表加锁,粒度大并发小
*行级锁:对行加锁,粒度小并发大
*间隙锁:间隙锁,锁住表的一个区间,间隙锁之间不会冲突只在可重复读下才生效,解决了幻读
基于属性:
共享锁:又称读锁,一个事务为表加了读锁,其它事务只能加读锁,不能加写锁
排他锁:又称写锁,一个事务加写锁之后,其他事务不能再加任何锁,避免脏读问题
内连接取量表交集部分,左连接取左表全部右表匹部分,右连接取右表全部坐表匹部分
用外连接的话连接顺序是固定的,比如left join,他必须先对左表进行全表扫描,然后一条条到右表去匹配;而内连接的话mysql会自己根据查询优化器去判断用哪个表做驱动。
子查询的话同样也会对驱动表进行全表扫描,所以尽量用小表做驱动表。
where是约束声明,having是过滤声明,where早于having执行,并且where不可以使用聚合函数(group by),having可以
第一范式:每个列都不可以再拆分。
第二范式:在第一范式的基础上,非主属性完全依赖于主键,而不能是依赖于主键的一部分。
第三范式:在第二范式的基础上,每一个非主属性既不部分依赖于码也不传递依赖于码。
Spring是个轻量级的框架,通过IOC达到松耦合的目的,通过AOP可以分离应用业务逻辑和系统服务进行内聚性的开发,不过配置各种组件时比较繁琐,所以后面才出选了SpringBoot的框架。
IOC是控制反转,是一种思想,把对象的创建和调用从程序员手中交由IOC容器管理,维护对象之间的依赖关系,降低对象之间的耦合度。 实现方式为DI依赖注入,有三种注入方式:构造器、setter、接口注入
创建一个bean的方式有xml方式、@Bean注解方式、@Componte方式
我们在对一个bean进行实例化后,要对他的属性进行填充,大多数我们都是使用 @Autowire直接的填充依赖注入的,他是有限按照类型进行匹配。
AOP面向切面编程。是spring两大核心之一,它是一种编程思想,是对OOP的一种补充。它可以对业务逻辑的各个部分进行隔离,降低耦合,提高代码的可重用性。它的底层是通过动态代理实现的。它的应用场景有事务、日志管理等。
AOP是面向切面编程,可以将那些与业务不相关但是很多业务都要调用的代码抽取出来,思想就是不侵入原有代码的情况下对功能进行增强。
SpringAOP是基于动态代理实现的,动态代理是有两种,一种是jdk动态代理,一种是cglib动态代理:
jdk动态代理是原理是利用反射来实现的,需要调用反射包下的Proxy类的newProxyInstance方法来返回代理对象,这个方法中有三个参数,分别是用于加载代理类的类加载器,被代理类实现的接口的class数组和一个用于增强方法的InvocaHandler实现类。
cglib动态代理原理是利用asm开源包来实现的,是把被代理类的class文件加载进来,通过修改它的字节码生成子类来处理
jdk动态代理要求被代理类必须有实现的接口,生成的动态代理类会和代理类实现同样的接口,cglib则,生成的动态代理类会继承被代理类。Spring默认使用jdk动态代理,当被代理的类没有接口时就使用cglib动态代理。
想要定义一个全局异常处理类的话,我们需要在这个类上添加**@ContaollerAdvice注解**,然后定义一些用于捕捉不同异常类型的方法,在这些方法上添加 @ExceptionHandler(value = 异常类型.class) 和 @ResponseBody注解,方法参数是HttpServletRequest和异常类型,然后将异常消息进行处理。
如果我们需要自定义异常的话,就写一个自定义异常类,该类需要继承一个异常接口,类属性包括final类型的连续id、错误码、错误信息,再根据需求写构造方法。
spring事务有编程式和声明式,我们一般使用声明式,在某个方法上增加@Transactional注解,这个方法中的sql会统一成功或失败。
原理是:
当一个方法加上@Transactional注解,spring会基于这个类生成一个代理对象并将这个代理对象作为bean,当使用这个bean中的方法时,如果存在@Transactional注解,就会将事务自动提交设为false,然后执行方法,执行过程没有异常则提交,有异常则回滚、
SpringMVC工作过程围绕着前端控制器DispatchServerlet,几个重要组件有HandleMapping(处理器映射器)、HandleAdapter(处理器适配器)、ViewReslover(试图解析器)
工作流程:
(1)DispatchServerlet接收用户请求将请求发送给HandleMapping
(2)HandleMapping根据请求url找到具体的handle和拦截器,返回给DispatchServerlet
(3)DispatchServerlet调用HandleAdapter,HandleAdapter执行具体的controller,并将controller返回的ModelAndView返回给DispatchServler
(4)DispatchServerlet将ModelAndView传给ViewReslover,ViewReslover解析后返回具体view
(5)DispatchServerlet根据view进行视图渲染,返回给用户
Spring mvc 是一个基于java的实现了mvc设计模式的轻量级web框架,在这种模式下软件被分为三层,即model、view、Controller。将软件分层的好处是 可 以将对象之间的耦合度降低,便于代码的维护。model封装了数据和对数据的操作,是实际进行数据处理的地方,view负责进行模型的展示,一般就是我们见 到的用户界面 Controller控制器负责视图和模型之间的交互,主要负责两方面的动作,一是把用户的请求分发到相应的模型,二是把模型的改变及时地响应到 视图上。Spring mvc框架已经成为了mvc模式地最主流实现,前端控制器是DispatcherServlet接口实现类,映射处理器是HandlerMapping接口实现类,视图 解析器是ViewResolver接口实现类,页面控制器是Controller接口实现类
启动类@SpringbootApplication注解下,有三个关键注解
(1)@springbootConfiguration:表示启动类是一个自动配置类
(2)@CompontScan:扫描启动类所在包外的组件到容器中
(3)@EnableConfigutarion:最关键的一个注解,他拥有两个子注解,其中@AutoConfigurationpackageu会将启动类所在包下的所有组件到容器中,@Import会导入一个自动配置文件选择器,他会去加载META_INF目录下的spring.factories文件,这个文件中存放很大自动配置类的全类名,这些类会根据元注解的装配条件生效,生效的类就会被实例化,加载到ioc容器中
@SpringBootApplication注解: 在Spring Boot入口类中,唯一的一个注解就是@SpringBootApplication。它是Spring Boot项目的核心注解,用于开启自动配置,准确说是通过该注解内组合的@EnableAutoConfiguration开启了自动配置。
@EnableAutoConfiguration注解: @EnableAutoConfiguration的主要功能是启动Spring应用程序上下文时进行自动配置,它会尝试猜测并配置项目可能需要的Bean。自动配置通常是基于项目classpath中引入的类和已定义的Bean来实现的。在此过程中,被自动配置的组件来自项目自身和项目依赖的jar包中。
@Import注解: @EnableAutoConfiguration的关键功能是通过@Import注解导入的ImportSelector来完成的。从源代码得知@Import(AutoConfigurationImportSelector.class)是@EnableAutoConfiguration注解的组成部分,也是自动配置功能的核心实现者。
@Conditional注解: @Conditional注解是由Spring 4.0版本引入的新特性,可根据是否满足指定的条件来决定是否进行Bean的实例化及装配,比如,设定当类路径下包含某个jar包的时候才会对注解的类进行实例化操作。总之,就是根据一些特定条件来控制Bean实例化的行为。
@RestController :修饰类,该控制器会返回Json数据
@RequestMapping(“/path”) :修饰类,该控制器的请求路径
@Autowired : 修饰属性,按照类型进行依赖注入
@PathVariable : 修饰参数,将路径值映射到参数上
@ResponseBody :修饰方法,该方法会返回Json数据
@RequestBody(需要使用Post提交方式) :修饰参数,将Json数据封装到对应参数中
@Controller@Service@Compont: 将类注册到ioc容器
@Transaction:开启事务
1、HTTPS 协议需要到 CA (Certificate Authority,证书颁发机构)申请证书,一般免费证书较少,因而需要一定费用。(以前的网易官网是http,而网易邮箱是 https 。)
2、HTTP 是超文本传输协议,信息是明文传输,HTTPS 则是具有安全性的 SSL 加密传输协议。
3、HTTP 和 HTTPS 使用的是完全不同的连接方式,用的端口也不一样,前者是80,后者是443。
4、HTTP 的连接很简单,是无状态的。HTTPS 协议是由 SSL+HTTP 协议构建的可进行加密传输、身份认证的网络协议,比 HTTP 协议安全。(无状态的意思是其数据包的发送、传输和接收都是相互独立的。无连接的意思是指通信双方都不长久的维持对方的任何信息。)
cd命令:切换目录
pwd命令:显示当前目录
mkdir命令:创建目录(文件夹)
rmdir命令:删除“空”目录
ls命令:文件与目录的查看
cp命令:复制文件或目录
ifconfig:查看网络接口详情
ping:查看与某主机是否能联通
ps -ef|grep 进程名称:查看进程号
lost -i 端口 :查看端口占用情况
top:查看系统负载情况,包括系统时间、系统所有进程状态、cpu情况
free:查看内存占用情况
kill:正常杀死进程,发出的信号可能会被阻塞
kill -9:强制杀死进程,发送的是exit命令,不会被阻塞
开放地址法:有线性探测法和平方探测法,当发生冲突时,继续往后找
再哈希法:构造多个哈希函数,发生冲突后使用下一个函数
链地址法:将hash值相同的记录用链表链接起来
建立公共溢出区:将哈希表分为基础表和益处表两部分,发生冲突的填入益处表
在数据库中有五张表,分别是菜单表,角色表,用户表,他们是多对多的关系,所以还有角色菜单表,角色用户表
登录后进入认证过滤器,获取用户名和密码,根据用户名查询用户具有的权限并把用户名和对应权限信息放到redis,JWT生成token后放入cookie,每次调用接口时携带
然后执行授权过滤器,从header中获取token解析出用户名,根据用户名从redis中获取权限列表,然后springSecurity就能够判断当前请求是否有权限访问
1.请求方式不匹配
2.json、x-wwww-form-urlencoded混乱的错误
3.前后端参数不一致,空指针异常,数据类型不匹配
4.mp生成的分布式id是19位,JavsScrip只会处理16位,将id生成策略改为String类型
5.跨域问题:跨域问题是在访问协议、ip地址、端口号这三个有任何一个不一样,相互访问就会出现跨域,可以通过Spring注解解决跨域的 @CrossOrigin,也可以使用nginx反向代理、网关
6.maven加载项目时,默认不会加载src-java文件夹的xml类型文件,可以 将xml放到resources文件夹下,也可以在yaml和pom中添加配置 。
常用的设计模式有单例模式、工厂模式、代理模式、适配器模式、装饰器模式、模板方法模式等等。像sping中的定义的bean默认为单例模式,spring中的BeanFactory用来创建对象的实例,他是工厂模式的体现。AOP面向切面编程是代理模式的体现,它的底层就是基于动态代理实现的。适配器模式在springMVC中有体现,它的处理器适配器会根据处理器规则适配相应的处理器执行,模板方法模式用来解决代码重复的问题等。
物理内存:计算机中真实拥有的内存。物理内存是有限的,容易产生内存不足问题。
虚拟内存是一种抽象的逻辑概念,拥有连续的内存地址。
1.抽象类多用于在同类事物中有 无法具体描述的方法的场景,而接口多用于不同类之间,定义不同类之间的通信规则。
2.接口只有定义,而抽象类可以有定义和实现。
3.接口需要实现implement,抽象类只能被继承extends,一个类可以实现多个接口,但一个类只能继承一个抽象类。
4.抽象类倾向于充当公共类的角色,当功能需要累积时,用抽象类;接口被运用于实现比较常用的功能,功能不需要累积时,用接口。
Java中接口和抽象类的定义语法分别为interface与abstract关键字。
相同点:
都不能被实例化 ,接口的实现类或抽象类的子类都只有实现了接口或抽象类中的方法后才能实例化
。
不同点:
1、抽象类中的抽象方法的修饰符只能为public或者protected,默认为public;接口中的方法默认使用public修饰
2、接口成员变量默认为public static final,必须赋初值,不能被修改。抽象类中成员变量默认default,可在子类中被重新定义,也可被重新赋值;
3、实现接口的关键字为implements,继承抽象类的关键字为extends。一个类可以实现多个接口,但一个类只能继承一个抽象类。所以,使用接口可以间接地实现多重继承。
4、接口强调特定功能的实现,而抽象类强调所属关系。
5、抽象类可以包含方法、构造方法,方法可以实现,但是构造方法不能用于实例化,主要用途是被子类调用。接口只有定义,不能有方法的实现,java1.8中可以定义default方法体
抽象类:
①使用abstract修饰的类或方法,就抽象类或者抽象方法
②抽象类是不能具体的描述一个对象,不能用抽象类直接实例化对象
③抽象类里面的成员变量和成员方法,都是和普通类一样的,只不过就是不能进行实例化了
④当一个普通类继承这个抽象类后,那么这个普通类必须重写抽象类当中的所有的抽象方法(我们之前说过抽象类是不具体的,没有包含足够的信息来描述一个对象,所以我们需要把他补充完整)
(1)HashTable的每个方法都用synchronized修饰,因此是线程安全的,但同时读写效率很低
(2)HashTable的Key不允许为null
(3)HashTable只对key进行一次hash,HashMap进行了两次Hash
(4)HashTable底层使用的数组加链表
解题思路
得分点: Java与C++的区别,Java的优点
标准回答 :Java是一门非常纯粹的面向对象的编程语言,它在吸收C++语言的各种优点的同时去除了C++语言中令人难以理解的多继承、指针等概念。所以Java语言在保证了强大的功能性的基础上,还比C++语言更为简单易用。Java语言极好地实现了面向对象理论,是静态面向对象编程语言的代表,它的存在保证了程序员可以用优雅的思维方式进行复杂的编程 。 Java还拥有平台独立性,可以做到"一次编译,到处运行"。java还提供了很多内置的类库,通过这些类库,简化了开发人员的程序设计工作,缩短了项目的开发时间,最重要的是Java提供了垃圾回收器,这也将开发人员从对内存的管理中解脱出来。同时Java拥有良好的安全性和健壮性,java语言经常被用在网络环境中,为了增强程序的安全性,java语言提供了一个防止恶意代码攻击的安全机制(数组边界检测和Bytecode校验等)。
java的强类型机制、垃圾回收器、异常处理和安全检查机制使得用java语言编写的程序有很好的健壮性。此外,Java还提供了对Web应用开发的支持:例如Applet、Servlet和JSP可以用来开发Web应用程序;Socket、RMI可以用来开发分布式应用程序的类库。
加分回答: Java为什么可以跨平台??
JVM(Java虚拟机)是Java跨平台的关键。 在运行程序之前,Java源代码(.java)需要经过编译器,将源代码翻译成字节码(.class),但字节码不能直接运行,所以必须通过JVM将字节码翻译成特定平台的机器码运行程序。但跨平台的是Java程序、而不是JVM,所以需要在不同平台下安装不同版本的JVM。
1、异常处理机制让程序具有容错性和健壮性,程序运行出现状况时,系统会生成一个Exception对象来通知程序
2、处理异常的语句由try、catch、finally三部分组成。try块用于包裹业务代码,catch块用于捕获并处理某个类型的异常,finally块则用于回收资源。
3、如果业务代码发生异常,系统创建一个异常对象,并将其提交给JVM,由JVM寻找可以处理这个异常的catch块,并将异常对象交给这个catch块处理。如果JVM没有找到,运行环境终止,Java程序退出。
4、Java也允许程序主动抛出异常。当业务代码中,判断某项错误的条件成立时,可以使用throw关键字向外抛出异常。
解题思路
得分点 :磁盘IO
标准回答 :首先,红黑树是一种近似平衡二叉树(不完全平衡),结点非黑即红的树,它的树高最高不会超过 2*log(n),因此查找的时间复杂度为 O(log(n)),无论是增删改查,它的性能都十分稳定; 但是,红黑树本质还是二叉树,在数据量非常大时,需要访问+判断的节点数还是会比较多,同时数据是存在磁盘上的,访问需要进行磁盘IO,导致效率较低; 而B+树是多叉的,可以有效减少磁盘IO次数;同时B+树增加了叶子结点间的连接,能保证范围查询时找到起点和终点后快速取出需要的数据。
加分回答:红黑树做索引底层数据结构的缺陷 试想一下,以红黑树作为底层数据结构在面对在些表数据动辄数百万数千万的场景时,创建的索引它的树高得有多高? 索引从根节点开始查找,而如果我们需要查找的数据在底层的叶子节点上,那么树的高度是多少,就要进行多少次查找,数据存在磁盘上,访问需要进行磁盘IO,这会导致效率过低; 那么红黑树作为索引数据结构的弊端即是:树的高度过高导致查询效率变慢。
解题思路
得分点 :Java中数据类型分类、八大数据类型
标准答案 :Java的数据类型分为基本数据类型和引用数据类型两大类。 基本数据类型共有八大类,这八大数据类型又可分为四小类,分别是整数类型(byte/short/int/long)、浮点类型(float、double)、字符类型(char)和布尔类型(boolean)。其中,int是最常用的整数类型,double是最为常用的浮点类型,除了布尔类型之外的其他7个类型,都可以看做是数字类型,它们相互之间可以进行类型转换。
引用类型包括数组、类、接口类型,还有一种特殊的null类型,所谓引用数据类型就是对一个对象的引用,对象包括实例和数组两种。
加分回答 对于基本数据类型,你需要了解每种类型所占据的内存空间,这是面试官喜欢追问的问题: - byte:1字节(8位),数据范围是 -2^7 ~ 2^7-1
。
-2^15 ~ 2^15-1
。-2^31 ~ 2^31-1
。-2^63 ~ 2^63-1
。-3.4*10^38 ~ 3.4*10^38
。-1.8*10^308 ~ 1.8*10^308
。\u0000 ~ \uffff
。静态变量: 也叫做类变量,是独立于方法之外的变量,它属于整个类,有static修饰。
只要程序加载了类的字节码,静态变量就会被分配空间并初始化,不用再去创建实例对象
调用方式:“类名.属性名” 或者 “对象名.属性名”,这两种方式都可以调用。
使用场合:所有对象的属性是一样的。
实例变量:也是独立于方法之外的变量,但它是属于某个对象的属性。
必须先创建了实例对象,其中的实例变量才会被分配空间,才能使用这个实例变量。
调用方式:只能通过“对象名.属性名” 的方式访问。
使用场合:所有对象的属性是不一样的。
线程锁: 目的是将一段代码锁住,一旦获得锁权限,除非释放线程锁,否则其他任何代码都无法获得锁权限
为什么需要线程锁
多线程同时在完成特定的操作时,由于并不是原子操作,所以在完成操作的过程中可能会被打断,去做其他的操作
可能产生脏数据
1、private 私有的
2、public 公共的
两者的区别
1.关键字private是默认访问级别,并且在所有其他访问级别中是最严格的。它给予一个类型或者类型成员最小的权限。私有成员仅仅在声明其的类体中可以被访问。
private:声明私有类,私有类自己的类可以使用(只能本类之中使用),其它类不可使用。
2.**public:**关键字public是所有访问级别中最自由地,没有任何访问限制。公共成员的访问不仅可以来自外部,也可以来自内部,并且可以自由访问定义在类体内或者体外的任何成员。
public:声明公共类,公共类其他类可以调用 (其它类中也可以调用)
是用java编写的一个服务器程序,目的是和浏览器交互并且生成动态的web内容。
Servlet广义上来讲指的是servlet接口,狭义上来讲指的是所有实现接口的实现类。
作用:
接收用户端发来的请求
调用其他java程序来处理请求
将处理结果,返回到服务器中
……
解题思路
得分点:数据结构、put()流程、扩容机制
标准回答 :数据结构 在JDK8中,HashMap底层是采用“数组+链表+红黑树”来实现的。 HashMap是基于哈希算法来确定元素的位置(槽)的,当我们向集合中存入数据时,它会计算传入的Key的哈希值,并利用哈希值取余来确定槽的位置。如果元素发生碰撞,也就是这个槽已经存在其他的元素了,则HashMap会通过链表将这些元素组织起来。如果碰撞进一步加剧,某个链表的长度达到了8,则HashMap会创建红黑树来代替这个链表,从而提高对这个槽中数据的查找的速度。
• 常见的请求方式有:get请求和post请求
• get一般用来进行查询操作,url地址有长度限制,请求的参数都暴露在url地址当中,如果传递中文参数,需要自己进行编码操作,安全性较低。
• post请求方式主要用来提交数据,没有数据长度的限制,提交的数据内容存在于http请求体中,数据不会暴漏在url地址中。
• post请求方式相对get请求方式来说要安全一些,但是速度慢
在以下情况中,请使用 POST 请求:
(1)无法使用缓存文件(更新服务器上的文件或数据库)
(2)向服务器发送大量数据(POST 没有数据量限制)
(3)发送包含未知字符的用户输入时,POST 比 GET 更稳定也更可靠