java基础知识点整理(持续更新)

1. Java基本数据类型,各占几个字节?

    Java一共有8种基本数据类型:int占4字节,short占2字节,long占8字节,byte占1字节,float占4字节,double占8字节,char占2字节,boolean占1字节。

 

2. Java反射机制的原理以及创建类实例的三种方式是什么?

    原理:JAVA反射机制是在运行状态中,对于任意一个类,都能够知道这个类的所有属性和方法;对于任意一个对象,都能够调用它的任意一个方法和属性;这种动态获取的信息以及动态调用对象的方法的功能称为java语言的反射机制。  

    创建类实例的三种方式:

    通过通过Class.forName("全类名")                                            

try {
			Class<?> perClazz = Class.forName("reflect_fanshe.Person");
			System.out.println(perClazz);
		} catch (ClassNotFoundException e) {
			
			e.printStackTrace();
		}

    类名.class

Class<?> perClazz2 = Person.class;

    对象.getClass()

 

3. JVM的内存模型

    将这张图记熟即可

 

 

4. redis的几种基本数据类型

     String(字符串)

     List(列表)

     Set(集合)

     Hash(哈希)

     Zset(sorted set:有序集合)

 

5. 高并发下如何安全修改同一行数据

    使用悲观锁,FIFO(First Input First Output,先进先出)缓存队列思路,使用乐观锁

 

6. spring mvc的执行流程 

       首先用户发送请求——>DispatcherServlet,前端控制器收到请求后自己不进行处理,而是委托给其他的解析器进行处理,作为统一访问点,进行全局的流程控制;

       DispatcherServlet——>HandlerMapping, HandlerMapping将会把请求映射为HandlerExecutionChain对象(包含一个Handler处理器(页面控制器)对象、多个HandlerInterceptor拦截器)对象,通过这种策略模式,很容易添加新的映射策略;

       DispatcherServlet——>HandlerAdapter,HandlerAdapter将会把处理器包装为适配器,从而支持多种类型的处理器,即适配器设计模式的应用,从而很容易支持很多类型的处理器;

       HandlerAdapter——>处理器功能处理方法的调用,HandlerAdapter将会根据适配的结果调用真正的处理器的功能处理方法,完成功能处理;并返回一个ModelAndView对象(包含模型数据、逻辑视图名);

       ModelAndView的逻辑视图名——> ViewResolver, ViewResolver将把逻辑视图名解析为具体的View,通过这种策略模式,很容易更换其他视图技术;

       View——>渲染,View会根据传进来的Model模型数据进行渲染,此处的Model实际是一个Map数据结构,因此很容易支持其他视图技术;

       返回控制权给DispatcherServlet,由DispatcherServlet返回响应给用户,到此一个流程结束。

    

7. 使用一种方式实现动态代理  

   JDK动态代理 

   利用反射机制生成一个实现代理接口的匿名类,在调用具体方法前调用InvokeHandler来处理

   CGlib动态代理

   利用ASM(开源的Java字节码编辑库,操作字节码)开源包,将代理对象类的class文件加载进来,通过修改其字节码生成子类来处理。

   区别

   JDK代理只能对实现接口的类生成代理;CGlib是针对类实现代理,对指定的类生成一个子类,并覆盖其中的方法,这种通过继承类的实现方式,不能代理final修饰的类。

   

  方式一:JDK动态代理

   目标接口类

/**
 * 目标接口类
 */
public interface UserManager {
    void addUser(String username, String password);
    void delUser(String username);
}

  接口实现类

/**
 * 动态代理:
 *      1. 特点:字节码随用随创建,随用随加载
 *      2. 作用:不修改源码的基础上对方法增强
 *      3. 分类:
 *              1)基于接口的动态代理
 *                      1. 基于接口的动态代理:
 *                              1)涉及的类:Proxy
 *                              2)提供者:JDK官方
 *                              3)如何创建代理对象:
 *                                      使用Proxy类中的newProxyInstance方法
 *                              4)创建代理对象的要求
 *                                      被代理类最少实现一个接口,如果没有则不能使用
 *                              5)newProxyInstance方法的参数:
 *                                      ClassLoader:类加载器,它是用于加载代理对象字节码的。和被代理对象使用相同的类加载器。固定写法。
 *                                      Class[]:字节码数组,它是用于让代理对象和被代理对象有相同方法。固定写法。
 *                                      InvocationHandler:用于提供增强的代码,它是让我们写如何代理。我们一般都是些一个该接口的实现类,通常情况下都是匿名内部类
 *              2)基于子类的动态代理
 */
public class JDKProxy implements InvocationHandler {
    // 用于指向被代理对象
    private Object targetObject;
    public Object newProxy(Object targetObject) {
        // 将被代理对象传入进行代理
        this.targetObject = targetObject;
        // 返回代理对象
        return Proxy.newProxyInstance(this.targetObject.getClass().getClassLoader(),this.targetObject.getClass().getInterfaces(),this);
    }

    /**
     * 被代理对象的任何方法执行时,都会被invoke方法替换,即:代理对象执行被代理对象中的任何方法时,实际上执行的时当前的invoke方法
     * @param proxy(代理对象的引用)
     * @param method(当前执行的方法)
     * @param args(当前执行方法所需的参数)
     * @return(和被代理对象方法有相同的返回值)
     * @throws Throwable
     */
    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        // 在原来的方法上增加了日志打印功能,增强代码
        printLog();
        Object ret = null;
        // 调用invoke方法(即执行了代理对象调用被调用对象中的某个方法)
        ret = method.invoke(targetObject, args);
        return ret;
    }

    /**
     * 模拟日志打印
     */
    private void printLog() {
        System.out.println("日志打印:printLog()");
    }
}

测试类

public class TestJDKProxy {
    public static void main(String[] args) {
        UserManager userManager = new UserManagerImpl();
        JDKProxy jdkProxy = new JDKProxy();
        UserManager userManagerProxy = (UserManager)jdkProxy.newProxy(userManager);
        System.out.println("--------------------没有使用增强过的方法--------------------");
        userManager.addUser("root","root");
        userManager.delUser("root");
        System.out.println("--------------------使用代理对象增强过的方法--------------------");
        userManagerProxy.addUser("scott","tiger");
        userManagerProxy.delUser("scott");
    }
}

测试结果

--------------------没有使用增强过的方法--------------------
调用了UserManagerImpl.addUser()方法!
调用了UserManagerImpl.delUser()方法!
--------------------使用代理对象增强过的方法--------------------
日志打印:printLog()
调用了UserManagerImpl.addUser()方法!
调用了UserManagerImpl.delUser()方法!

 

方式二:CGlib动态代理

/**
 * 动态代理:
 *      1. 特点:字节码随用随创建,随用随加载
 *      2. 作用:不修改源码的基础上对方法增强
 *      3. 分类:
 *              1)基于接口的动态代理
 *              2)基于子类的动态代理
 *                      1. 基于子类的动态代理:
 *                              1)涉及的类:Enhancer
 *                              2)提供者:第三方cglib库
 *                              3)如何创建代理对象:
 *                                      使用Enhancer类中的create方法
 *                              4)创建代理对象的要求
 *                                      被代理类不能是最终类
 *                              5)create方法的参数:
 *                                      Class:字节码,它是用于指定被代理对象的字节码。固定写法。
 *                                      Callback():用于提供增强的代码,它是让我们写如何代理。我们一般都是些一个该接口的实现类。固定写法。
 */
public class CGLibProxy implements MethodInterceptor {
    // 用于指向被代理对象
    private Object targetObject;

    // 用于创建代理对象
    public Object createProxy(Object targetObject) {
        this.targetObject = targetObject;
        return new Enhancer().create(this.targetObject.getClass(),this);
    }

    /**
     * 
     * @param proxy(代理对象的引用)
     * @param method(当前执行的方法)
     * @param args(当前执行方法所需的参数)
     * @param methodProxy(当前执行方法的代理对象)
     * @return(和被代理对象方法有相同的返回值)
     * @throws Throwable
     */
    @Override
    public Object intercept(Object proxy, Method method, Object[] args, MethodProxy methodProxy) throws Throwable {
        Object ret = null;
        // 过滤方法
        if ("addUser".equals(method.getName())) {
            // 日志打印
            printLog();
        }
        ret = method.invoke(targetObject, args);
        return ret;
    }

    /**
     * 模拟日志打印
     */
    private void printLog() {
        System.out.println("日志打印:printLog()");
    }
}

测试类

public class TestCGLibProxy {
    public static void main(String[] args) {
        CGLibProxy cgLibProxy = new CGLibProxy();
        UserManager userManager = new UserManagerImpl();
        UserManager cgLibProxyProxy = (UserManager)cgLibProxy.createProxy(userManager);
        System.out.println("--------------------没有使用增强过的方法--------------------");
        userManager.addUser("root","root");
        userManager.delUser("root");
        System.out.println("--------------------使用代理对象增强过的方法--------------------");
        cgLibProxyProxy.addUser("scott","tiger");
        cgLibProxyProxy.delUser("scott");
    }
}

测试结果

--------------------没有使用增强过的方法--------------------
调用了UserManagerImpl.addUser()方法!
调用了UserManagerImpl.delUser()方法!
--------------------使用代理对象增强过的方法--------------------
日志打印:printLog()
调用了UserManagerImpl.addUser()方法!
调用了UserManagerImpl.delUser()方法!

 

8. 悲观锁和乐观锁的区别,InnoDB的标准行级锁有哪两种,解释其含义

    悲观锁(Pessimistic Lock):

    每次拿数据的时候都会担心会被别人修改(疑心重很悲观),所以每次在拿数据的时候都会上锁。确保自己使用的过程中不会被别人访问,自己使用完后再解锁。

期间需要访问该数据的都会等待。

    乐观锁(Optimistic Lock):

    每次拿数据的时候都完全不担心会被别人修改(心态好很乐观),所以每次在拿数据的时候都不会上锁。但是在更新数据的时候去判断该期间是否被别人修改过(使用版本号等机制),期间该数据可以随便被其他人读取。

    两种锁各有优缺点,不能单纯的定义哪个好于哪个。乐观锁比较适合数据修改比较少,读取比较频繁的场景,即使出现了少量的冲突,这样也省去了大量的锁的开销,故而提高了系统的吞吐量。但是如果经常发生冲突(写数据比较多的情况下),上层应用不不断的retry,这样反而降低了性能,对于这种情况使用悲观锁就更合适。    

    InnoDB实现了以下两种类型的行锁:

  • 共享锁(S):允许一个事务去读一行,阻止其他事务获得相同数据集的排他锁。
  • 排他锁(X):允许获得排他锁的事务更新数据,阻止其他事务取得相同数据集的共享读锁和排他写锁。

 

9. 解释一下浅拷贝和深拷贝的区别

    浅拷贝: 
    被复制对象的所有变量都含有与原来的对象相同的值,而所有的对其他对象的引用仍然指向原来的对象。即对象的浅拷贝会对“主”对象进行拷贝,但不会复制主对象里面的对象。”里面的对象“会在原来的对象和它的副本之间共享。简而言之,浅拷贝仅仅复制所考虑的对象,而不复制它所引用的对象。

    深拷贝:
    深拷贝是一个整个独立的对象拷贝,深拷贝会拷贝所有的属性,并拷贝属性指向的动态分配的内存。当对象和它所引用的对象一起拷贝时即发生深拷贝。深拷贝相比于浅拷贝速度较慢并且花销较大。简而言之,深拷贝把要复制的对象所引用的对象都复制了一遍。

 

10. LinkedList和ArrayList的区别

        ArrayList是实现了基于动态数组的数据结构,LinkedList基于链表的数据结构。 
        对于随机访问get和set,ArrayList优于LinkedList,因为ArrayList可以随机定位,而LinkedList要移动指针一步一步的移动到节点处。(参考数组与链表来思考)
        对于新增和删除操作add和remove,LinedList比较占优势,只需要对指针进行修改即可,而ArrayList要移动数据来填补被删除的对象的空间。

 

11. redis如何保证数据一致性

        方式一:先更新数据库,再更新缓存场景
        方式二:先更新缓存,再更新数据库场景
        方式三:先删除缓存,再更新数据库的场景
        方式四:先更新数据库,在删除缓存场景
        方式五:最佳实现,数据异步同步

 

12. sql优化的一般步骤

      通过show status命令了解各种SQL的执行频率

      定位执行效率较低的SQL语句

      通过EXPLAIN分析较低SQL的执行计划

      通过show profile分析SQL

      通过trace分析优化器如何选择执行计划

      确定问题并采取相应的优化措施

 

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值