JAVA基础知识

Java 内存结构(运行时数据区)
1、堆(heap):
一个JVM虚拟机只有一个堆区;
它是在JVM启动时被创建;
它是被所有线程共享的;
堆是CG主要管理的区域(对不要用的对象进行标记和清除)
存储的内容:对象的实例(new一个对象才会存放在堆中)和数组。
CG 回收机制

2、栈(stock):
每个线程都有一个栈区,且相互之间不能够调用;
栈的数据结构类似于链表,先进后出(类似于Java方法,后进入的方法区先执行结束)
生命周期随着线程的创建而创建,随着线程的摧毁而消失(方法结束,局部变量失效、从栈中清除)。
存储的内容:基本数据类型的对象和自定义Class的引用(引用的对象存放在堆区)

3 方法区:
存放的内容:被虚拟机加载的类(.class)、常量、静态变量

问:String s = new String(“xyz”) 创建了几个字符串对象?
答:一个或两个。如果字符串常量池已经有“xyz”,则是一个;否则,两个。

当字符创常量池没有 “xyz”,此时会创建如下两个对象:

一个是字符串字面量 “xyz” 所对应的、驻留(intern)在一个全局共享的字符串常量池中的实例,此时该实例也是在堆中,字符串常量池只放引用。

另一个是通过 new String() 创建并初始化的,内容与"xyz"相同的实例,也是在堆中。

数据结构
HashMap:现在用的都是 JDK 1.8,底层是由“数组+链表+红黑树”组成,而在 JDK 1.8 之前是由“数组+链表”组成。主要是为了提升在 hash 冲突严重时(链表过长)的查找性能,使用链表的查找性能是 O(n),而使用红黑树是 O(logn)。当同一个索引位置的节点在新增后达到9个(阈值8):如果此时数组长度大于等于 64,则会触发链表节点转红黑树节点(treeifyBin);

Synchronized 的用法 :
synchronized可以用来修饰 实例方法、静态方法和代码块;
synchronized修饰实例方法时;锁的是这个方法,作用的范围是这个对象(当多个线程使用不同对象访问这个方法是不会同步、不会被锁)
synchronized修饰静态方法时,锁的是这个类所有的静态方法(非静态方法不会被锁),作用的对象是这个类(也就是说只有当前线程执行完所有的静态方法时,才会解锁。)
synchronized修饰代码块(分为三种情况):
1、synchronized(this){
//代码内容
} 锁的代码块里面的内容,作用的对象是当前对象。(不同对象访问是不会被锁)
2、synchronized(obj){
} 锁的是代码块的内容,如果两个线程的obj 是同一个实例 a.obj==b.obj 就会被锁 ,如果是不同的实例就不会被锁。
3、synchronized(A.class){
}锁的时代码块的内容,而作用的对象是这个类,也就是说不同的线程不同的实例访问也会被锁。

synchronized 和 Lock的区别:
1、synchronized 可以给类、方法、代码块加锁; Lock只能给代码块加锁。
2、synchronized 不需要手动获取锁和释放锁,当线程出现异常时,synchronized 会自动释放锁,不会造成死锁;而Lock 需要自己加锁和手动释放锁(unLock()方法)。
3、Lock可以查看是否成功获取锁。

创建线程有哪几种方式?
1、继承Thread 类 (Runable接口的实现类)。
2、实现Runable接口,并重写run()方法。
3、使用Executor框架创建线程池。Executor框架是juc里提供的线程池的实现。

public static void main(String[] args) {
//创建四个线程
new Ticket().start();
new Ticket().start();
new Ticket().start();
new Ticket().start();

}

}

class Ticket extends Thread {

private static int tickets = 10000;
static Ticket ticket = new Ticket();
public  void run () {
    while (true){
    synchronized (ticket){

            if(tickets>0){
                System.out.println(Thread.currentThread().getName() + "...这是第" + tickets-- + "号票");
            }
        }
        try {
            sleep(10);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
    }
    
}

注意:使用synchronized修饰的方法区内,是用sleep() 方法不会释放锁。(如果要验证多线程售票情况,将sleep()方法写在synchronized的方法区外)

多线程情况的单例模式 :
首先,单例模式是指某个类只实例化一次,而且自行实例化并像系统提供这个实例。
用private修饰实例化对象。当系统在创建这个对象时需要通过这个对象的实例化方法创建对象(不能通过new 一个对象)。

单例模式又可以分为懒汉模式跟饿汉模式;
懒汉模式就是在实例化这个对象的时候将对象设置为null,在实例化方法再创建这个对象。但是在多线程的情况下会存在线程安全的问题,所以在实例化对象时使用synchronized修饰实例化方法或者在new 对象时加synchronized 修饰。

1、在getInstance方法上加同步

public static synchronized Singleton getInstance() {
if (single == null) {
single = new Singleton();
}
return single;
}

2、双重检查锁定

/*双重校验锁实现单例模式 */
public class Singleton {
private static volatile Singleton singleton = null;

public Singleton() {

}
public static Singleton  getInstance() {
    if (singleton == null) {
        synchronized (Singleton.class) {
            if (singleton == null) {
                singleton = new Singleton();
            }
        }
    }
    return singleton;
}

}
另外,需要注意 Singleton 采用 volatile 关键字修饰也是很有必要。
Singleton 采用 volatile 关键字修饰也是很有必要的, Singleton = new Singleton (); 这段代码其实是分为三步执行:
1、为 Singleton 分配内存空间
2、初始化 Singleton
3、将 Singleton 指向分配的内存地址
但是由于 JVM 具有指令重排的特性,执行顺序有可能变成 1->3->2。指令重排在单线程环境下不会出先问题,但是在多线程环境下会导致一个线程获得还没有初始化的实例。例如,线程 T1 执行了 1 和 3,此时 T2 调用 getInstance() 后发现 Singleton 不为空,因此返回 Singleton ,但此时 Singleton 还未被初始化。

3、 静态内部类

public class Singleton {
private static class LazyHolder {
private static final Singleton INSTANCE = new Singleton();
}
private Singleton (){}
public static final Singleton getInstance() {
return LazyHolder.INSTANCE;
}
}
这种比上面1、2都好一些,既实现了线程安全,又避免了同步带来的性能影响。

而饿汉模式是在实例化对象时直接
private 对象 = new 对象(); 这种不存在线程安全的问题

为什么要用线程池?

1、降低了资源的消耗(可以重复利用线程池中的线程 )
2、提高系统响应速率(当任务到达是不需要等待线程的创建,可以直接使用线程池中的线程)
3、提高了线程的可管理性(使用线程池可以统一调用、管理、分配)

如何创造线程池?
通过Executor框架的工具类Executors实现:
//创建一个单线程的线程池
ExecutorService es=Executors.newSingleThreadExecutor();

//创建一个固定大小的线程池
ExecutorService es1=Executors.newFixedThreadPool(2);

//创建一个可缓存的线程池
ExecutorService es2=Executors.newCachedThreadPool();

//创建一个大小无限的线程池
ExecutorService es3= Executors.newScheduledThreadPool(2);

反射

对Spring的IOC理解:
IOC就是控制反转,将对象的控制权转交给Spring框架进行管理,并由Spring根据配置文件去创建实例和管理实例与实例之间的依赖关系。
优点:对象与对象之间的耦合松散,有利于功能的复用。

Spring的IOC有三种注入方式:构造器注入、setter注入、基于注解注入。

<bean id="blog" class="org.spring.ioc.entity.Blog">
    <property name="name" value="spring-ioc"/>
    <property name="content" value="spring"/>
    <property name="date" value="1520232449944"/>
    <property name="author" ref="author"/>
</bean>

<bean id="author" class="org.spring.ioc.entity.Author">
    <property name="name" value="luoliang"/>
    <property name="age" value="18"/>
    <property name="url" value="https://luoliangdsga.github.io"/>
</bean>
<bean id="user1" class="org.spring.ioc.entity.User">
    <constructor-arg index="0" value="1234"/>
    <constructor-arg index="1" value="spring"/>
</bean>

<!--使用type属性显式指定简单类型的构造器参数类型,这里对应的是User类中传入name,age的构造器-->
<bean id="user2" class="org.spring.ioc.entity.User">
    <constructor-arg type="java.lang.String" value="spring"/>
    <constructor-arg type="java.lang.Integer" value="20"/>
</bean>

<!--也可以使用构造器参数命名来指定值的类型-->
<bean id="user3" class="org.spring.ioc.entity.User">
    <constructor-arg name="id" value="1234"/>
    <constructor-arg name="name" value="spring"/>
</bean>

基于注解注入
@Component 可以注册所有的Bean (华南项目主要用于Plus和Filter)
@Repository 用于注册dao层的Bean(华南项目未用 直接New一个DTO)
@Controller 注册控制层的Bean
@Service 注册服务层的Bean

Spring AOP的理解:
AOP是指面向切面编程,是面向对象编程的一个补充,将那些对多个对象产生影响的行为和逻辑,抽取并封装成一个可重用的模块。 减少了代码的重复,降低了模块之间的耦合度,提高了系统的可维护性。一般用于权限认证、日志和事务管理。
核心原理:使用代理模式在方法执行前后或者出现异常时动态的加入与业务无关的逻辑。

AOP是实现方式:代理;
代理分为静态代理和动态代理;

静态代理: 1、目标对象必须要实现接口;
2、代理对象必须与目标对象实现同样的接口。

动态代理:
1、JDK动态代理 :动态代理的原理是程序运行期间通过反射机制实现的,代理类与委托类的关系是运行是才确定的。(委托类必须实现接口)。

2、cglib动态代理:当目标类没有实现接口时,可以使用Cglib动态代理。

Cglib是在运行期间动态的继承目标类实现的动态代理。

Spring 容器的启动流程:
1、初始化Spring容器 ,将Bean转化为内部数据结构BeanDefinition;
2、将配置类的BeanDefinition注册到容器中;
3、调用refresh()方法刷新(或加载)容器中的Bean。

BeanFactory 和FactoryBean的区别与作用
BeanFactory是一个工厂,所有的Bean都是由BeanFactory工厂生产(getBean())并管理的。
BeanFactory也是一个接口,IOC容器的核心接口,负责实例化、定位与配置应用程序中的对象并建立这些对象之间的依赖。

FactoryBean是 一个工厂Bean,可以返回Bean的实例(getObject())。这个Bean是FactoryBean new的Bean,所以不需要将这个Bean注册到IOC容器中。

通常情况下,bean 无须自己实现工厂模式,Spring 容器担任工厂 角色;但少数情况下,容器中的 bean 本身就是工厂,作用是产生其他 bean 实例。由工厂 bean 产生的其他 bean 实例,不再由 Spring 容器产生,因此与普通 bean 的配置不同,不再需要提供 class 元素。

BeanFactory和ApplicationContext有什么区别?
BeanFactory 和ApplicationContext 都是用来注册bean的接口,ApplicationContext接口是BeanFactory接口的子接口,ApplicationContext接口不仅实现了BeanFactory所具有的功能外(见上面),还实现了
继承MessageSource,因此支持国际化。
资源文件访问,如URL和文件(ResourceLoader)。
载入多个(有继承关系)上下文(即同时加载多个配置文件) ,使得每一个上下文都专注于一个特定的层次,比如应用的web层。
提供在监听器中注册bean的事件。
在注册Bean的时候,BeanFactory是通过getBean()方法单个注册所需要的Bean,ApplicationContext是在容器启动时一次性注册所有的Bean。

Spring Bean的生命周期?
1、实例化Bean:对于BranFactory,当用户向容器请求一个尚未初始化的Bean或者初始化这个Bean是有尚未注册的依赖时,容器会调用CreatBean()进行实例化;而对于ApplicationContext,容器在启动结束后,通过获取BeanDefinition中的信息,实例化所有的Bean。

2、设置对象属性(注入依赖):实例化Bean后Spring会根据BeanDefinition中的信息对Bean的属性初始化和注入依赖。

3、处理Aware相关接口:初始化实例后Spring会检测Bean是否实现的Aware相关接口的方法。

4、BeanPostProcess前置处理:如果想对Bean进行一些前置处理,可以实现BeanPostProcesssor接口进行处理。将会调用postProcessBeforeInitialization(Object obj, String s)方法。

5、InitializingBean:如果Bean实现了InitializingBean接口,执行afeterPropertiesSet()方法。

6、init-method:如果Bean在Spring配置文件中配置了 init-method 属性,则 会自动调用其配置的初始化方法。

7、BeanPostProcessor后置处理:如果这个Bean实现了BeanPostProcessor接口,将会调用postProcessAfterInitialization(Object obj, String s)方法;由于这个方法是在Bean初始化结束时调用的,所以可以被应用于内存或缓存技术;

8、DisposableBean:当Bean不再需要时,会经过清理阶段,如果Bean实现了DisposableBean这个接口,会调用其实现的destroy()方法;

9、destroy-method:最后,如果这个Bean的Spring配置中配置了destroy-method属性,会自动调用其配置的销毁方法。

什么是微服务?
微服务是一个独立的、职责单一的服务应用程序;一个服务只需要关注一个业务模块(专注某个功能的实现);例如登录系统只需要关注用户登录方面功能的实现。而微服务架构系统是一个分布式系统,根据不同的业务系统划分不同的服务单元模块,用于解决单个系统不足以满足越来越复杂的业务需求。

微服务之间的通讯:
同步通讯:一、dubbo :远程服务调用的分布式框架 RPC远程服务调用

Provider : 服务提供方
Consunmer : 服务的调用方
Registry: 服务注册与发现的注册中心(使用zookeeper作为注册中心)
Monitor : 服务调用次数与服务调用时间的监控中心
Container:服务的运行容器
步骤0:服务容器负责启动、加载、运行服务提供者。
1:Provider将提供的服务注册到注册中心。
2: Consumer 向注册订阅所需要的服务。
3:服务注册中心提供给Consumer 所需要服务的地址列表。
4:消费者根据注册中心所提供的地址队列选择其中一个进行服务的调用。(基于软负载均衡算法)
5:服务消费者和提供者,在内存中累计调用次数和调用时间,定时每分钟发送一次统计数据到监控中心。
二 、SpringCloud :springcloud通过 REST 接口json调用
异步通讯:消息队列 (RabbitMQ、RocketMQ)

SpringCloud 和 Dubbo 有哪些区别?
dubbo采用的RPC的远程服务调用的方式;
springCloud采用的是基于HTTP的REST的方式。

SpringBoot 和 SpringCloud 之间关系?
SpringCloud是基于SpringBoot的一套实现微服务的框架,SpringBoot主要单个微服务所使用的技术框架,而SpringCloud是将一个个微服务组合并管理起来。
SpringBoot 可以脱离SpringCloud单独使用;但SpringCloud不能够脱离SpringBoot使用。

Redis

Redis内部的五种数据结构:
1、String
2、链表(List):Redis内部的链表 相当于LinkedList,插入的时间很快(链表内部的Pre和next属性),查询的时间慢(需要遍历查询O(n))
3、集合(Set):相当于HashSet ,存储唯一的无序集合。(内部数据结构为HashMap,键为存储的数据,值为null);
4、数组(MAP);相当于HashMap,存储的键值对数据,无序唯一。
5、有序集合(Zset):内部结构为ScoreSet+HashMap ,一方面Set保证了数据的唯一性,给每个Value一个Score进行排序。

Redis的持久化
1、RDB持久化:
通过快照(压缩的二进制文件)的形式保存某个时间点Redis内的数据。是Redis的默认持久化方式。(RDB文件体积小,传输快,但容易造成数据的丢失,对性能的影响小)
2、AOF(append-only-file)持久化:
记录所有修改数据库的命令,以append的形式追加到AOF文件中。(下次服务器启动时,会加载AOF文件使数据库恢复到服务器关闭前的数据)(AOF文件体积大,恢复的速度慢,但能确保数据的准确性和完整性(数据不会丢失))

Redis会出现的问题与解决方案:
1、缓存穿透:
多次查询一个一定不存在的数据,每一次查询都会访问数据库,可能造成数据库挂掉。
解决:当查询的数据为空时,在缓存中同样的存一个null值,但是过期的时间设短一点。

2、缓存击穿:
对于设置了过期时间的Key,当到达某个临界过期的时间,恰好大并发的请求访问这个Key,就会大并发的访问数据库,造成数据库的崩溃。
解决方案:使用互斥锁,当缓存过期时,不立刻访问DB,而是使用互斥锁先让一个线程访问,并将返回结果存入缓存。

3、缓存雪崩:
对于设置了相同过期时间的Key,当到达某个临界过期的时间,恰好大并发的请求访问这些Key,就会大并发的访问数据库,造成数据库的崩溃。
解决方案:设置过期时间时,在过期时间后加上一个1-5分钟是随机时间,避免过多的key在同一时间失效。

Redis的集群模式(多个Redis数据库,一个主数据库,多个从数据库)
1、主从复制:
当从数据库启动时,会向主数据库发送sync命令,主数据库在收到sync命令后会在后台保存快照rdb,同时会将保存期间的数据缓存起来,一起发给从数据库,后没收到一条命令都会同步发给从数据库。

2、哨兵模式:
监视主、从数据库是否正常运行。
出现故障自动将从数据库转为主数据库。

Redis的内存淘汰机制
allkeys-lru:当内存不足以容纳新写入数据时,在键空间中,移除最近最少使用的 key(这个是最常用的)。
volatile-ttl:当内存不足以容纳新写入数据时,在设置了过期时间的键空间中,有更早过期时间的 key 优先移除。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值