java面试知识点

写在开头

在面试准备以及面试中碰到的一些知识点的积累,并没有做太全面的知识点收集和整理,写于博客用以后续巩固。

正文开始

1、分布式session的一致性有哪些解决方案

1.使用cookie代替session(不安全,不推荐使用)
2.使用数据库存储session(效率低,不推荐使用)
3.使用nginx反向代理ip绑定方法,同一个ip只能在同一台服务器上进行访问(不推荐,相当于没有集群)。
4.使用Spring-Session框架,相当于把session缓存到redis中。
5.使用Tomcat内置的对session的同步(有延迟,不推荐)
6.使用Token代替session,token存放在redis中,实现分布式共享。

2、前后端分离的问题:导致出现跨域

1、同源情况:无区别
2、跨域:
1)通过jsonp,但jsonp本身没有跨域安全规范,一般是后端进行安全控制,容易造成安全问题
2)CORS解决:@CrossOrigin允许跨域,origins-允许跨域请求的域,allowCredentials-允许设置和接收Cookie

3、rabbitmq

1、五种队列

1)简单队列:生产者将消息发送到队列,消费者从队列中获取队列
2)work模式:一个生产者两个消费者
一个消息只能被一个消费者获取
轮询分发:循环发送,不管消费者的消费损耗
公平分发:一次只发一条消息;设置:开启手动确认、手动返回完成状态
3)订阅模式
一个生产者多个消费者;生产者将消息发送到交换机;消费者绑定的队列与交换机绑定;生产者发送消息到交换机,然后到达队列,实现一个消息被多个消费者获取的目的
。。。如果消息发送到没有与队列绑定的交换机时将会丢失消息。
fanout:多个队列绑定多个交换机,生产者发送消息到多个交换机
direct:一个direct类型的交换机,声明一个路由键;消费者绑定这个路由键,就可以消费指定类型的消息
topic:

2、rabbitmq消息丢失解决方案

1)生产者将数据发送到rabbitmq时期
可以采用rabbitmq的事务机制或confirm机制解决;
事务机制:即在生产者发送数据之前开启rabbitmq事务channel.teSelect,然后发送消息,如果消息没有成功被rabbitmq接收到,那么生产者会受到异常报错,此时回滚事务
channel.txSelect
try{
//发送消息
}catch(Exception e){
channel.txRollback
}
channel.txCommit
comfirm机制:在生产者设置开启confirm机制后,每次写的消息都会分配唯一的id,如果写入了rabbitmq中,会回传一个ack消息,告诉你说这个消息ok了,如果rabbitmq没处理这个消息,会回调一个nack借口,告诉你这个消息接收失败,你可以重试。而且可以结合这个机制在内存里维护每个消息id的状态,如果超过一定的实践没有接收到消息的回调,那么可以重发,因为confirm模式是异步调用的,所以性能各方面都比事务机制好。
2)rabbitmq丢失数据,
开启持久化,即消息写入后持久化到磁盘。
持久化的两个步骤(同时设置):
(1)创建queue的时候将它设置为持久化,但是只是持久化queue的元数据,不会持久化queue里面的数据;
(2)设置deliveryMode为2,将数据持久化,RabbitMQ会将数据持久化到磁盘中。
3) 消费者丢失数据:消费端刚消费到,但是还没处理完,进程挂掉或者服务器关机等原因都可能导致数据丢失,这个时候就得用到RabbitMQ提供的ack机制,但是得关闭RabbitMQ自动ack,通过手动调取接口形式,自己处理完在ack。

4、基本数据类型

byte字节型:1个字节 -128~127
short短整型:2个字节 -2的15次幂~2的15次幂-1
int整型:4个字节
long长整型:8个字节
float单精度浮点型:4个字节
double双精度浮点型:8个字节
char字符型:2个字节
boolean布尔型:1个字节

5、树的一种遍历方式

class TreeNode {
    int val = 0;
    TreeNode left = null;
    TreeNode right = null;

    public TreeNode(int val) {
        this.val = val;
    }
}

public class StrToSum {
        public ArrayList<Integer> preOrder(TreeNode root) {
            ArrayList<Integer> list = new ArrayList<>();
            Stack<TreeNode> stack = new Stack<>();
            stack.push(root);
            while(!stack.empty()){
                TreeNode treeroot = stack.pop();
                list.add(treeroot.val);
                if(treeroot.right!=null){
                    stack.push(treeroot.right);
                }
                if(treeroot.left!=null){
                    stack.push(treeroot.left);
                }
            }
            return list;
        }

    public static void main(String[] args) {
        TreeNode a = new TreeNode(0);
        TreeNode b = new TreeNode(1);
        TreeNode c = new TreeNode(2);
        TreeNode d = new TreeNode(3);
        TreeNode e = new TreeNode(4);
        TreeNode f = new TreeNode(5);
        TreeNode g = new TreeNode(6);
        a.left = b;
        a.right = c;
        b.left = e;
        b.right = g;
        c.left = f;
        c.right = g;
        StrToSum t = new StrToSum();
        ArrayList<Integer> list = new ArrayList<>();
        list = t.preOrder(a);
//      list=t.PrintFromTopToBottom(a);
//      list= t.preOrder(a);
//        list = t.postOrder(a);
//      list= t.inOrder(a);
        for (Integer item : list){
            System.out.println(item);
        }
    }
}
相关Stack类

栈,先进后出的方式
push(E item):把栈压入堆栈顶部
pop():移除堆栈顶部的对象,并作为此函数的值返回
peek():查看堆栈顶部的对象,但不移除
boolean empty():测试堆栈是否为空
int search(Object o):返回对象在占中的位置,以1为基数

相关Queue类

队列:先进先出
add(E item):在尾部添加;方法添加失败(比如队列已满)报运行时错误
offer(E item):在尾部添加;方法添加失败时返回false
remove():删除并返回头部;队列为空时报NoSuchElementException
poll():删除并返回头部;队列为空时返回null
element():获取但不删除;队列为空时抛出异常
peek():获取但不删除;队列为空时返回null

6、java中的栈和堆

java把内存划分成两种:一种是栈内存,一种堆内存
1)在函数中定义的一些基本类型的变量和对象的引用变量都在函数的栈内存中分配。
当在一段代码块定义一个变量时,java就在栈中为这个变量分配内存空间,当超过变量的作用域后java会自动释放掉为该变量所分配的内存空间,该内存空间可以立即被另作他用。

2)堆内存用来存放由new创建的对象和数组
在堆中分配的内存,由java虚拟机的自动垃圾回收器来管理。在堆中产生了一个数组或对象后,还可以在栈中定义一个特殊的变量,让栈中这个变量的取值等于数组或对象在堆内存中的首地址,栈中的这个变量就成了数组或对象的引用变量。 引用变量就相当于是为数组或对象起的一个名称,以后就可以在程序中使用栈中的引用变量来访问堆中的数组或对象

3)栈与堆都是java用来在ram中存放数据的地方,java自动管理栈和堆,程序员不能直接的设置栈或堆;java的堆是一个运行时数据区,他们不需要程序代码来显式的释放,

4)堆是由垃圾回收负责的,堆的优势是可以动态的分配内存大小,生存期也不必事先告诉编译器,是在运行时动态分配内存,java的垃圾收集器会自动收走这些不再使用的数据,缺点是要在运行时动态分配内存,存取速度较慢。

5)栈的优势:存取速度比堆要快,仅次于寄存器,栈数据可以共享,但缺点是存在栈中的数据大小与生存期必须是确定的,缺乏灵活性,栈主要存放一些基本类型的变量和对象句柄。
重要的特殊性:栈中的数据可以共享。
编译器先处理int a = 3;首先它会在栈中创建一个变量为a的引用,然后查找栈中是否有3这个值,如果没找到,就将3存放进来,然后将a指向3。接着处理int b = 3;在创建完b的引用变量后,因为在栈中已经有3这个值,便将b直接指向3。这样,就出现了a与b同时均指向3的情况。这时,如果再令a=4;那么编译器 会重新搜索栈中是否有4值,如果没有,则将4存放进来,并令a指向4;如果已经有了,则直接将a指向这个地址。因此a值的改变不会影响到b的值。要注意这 种数据的共享与两个对象的引用同时指向一个对象的这种共享是不同的,因为这种情况a的修改并不会影响到b, 它是由编译器完成的,它有利于节省空间。而一个对象引用变量修改了这个对象的内部状态,会影响到另一个对象引用变量。

6)比较类里面的数值是否相等时,用equals()方法;当测试两个包装类的引用是否指向同一个对象时,用==

7)JVM运行时,将内存分为堆和栈,栈中存放的是创建的对象,JAVA字符串对象内存实现时,在堆中开辟了一块很小的内存,叫字符串常量池,用来存放特定的字符串对象

7、tomcat的常用配置

1)修改端口号
2)修改tomcat的编码URIEncoding=‘UTF-8’
3)设置tomcat的热部署:
docBase:项目的相对路径或绝对路径,相对路径是相对于webapps
path:访问项目的路径
reloadable:是否自动加载新增或改变的文件
debug:输出日志的详细程度
4)配置tomcat使用的JDK路径

8、hibernate的延迟加载机制

在hibernate方法中,直接涉及到延迟加载的方法有get和load,使用get时,不会延迟加载,load则反之。在hibernate设置延迟加载后,hibernate返回给我们的对象(要延迟加载的对象)是一个代理对象,并不是真实的对象,该对象没有真实对象的数据,只有真正需要用到对象数据(调用getter等方法时)时,才会触发hibernate去数据库查对应数据,而且查回来的数据不会存储在代理对象中,所以这些数据是无法在调试窗口查看到的。

9、spring的事务实现方式

1)编程式事务管理,需要在代码中调用beginTransaction()/commit()/rollback()d等事务管理相关的方法
2)基于TransactionProxyFactoryBean的声明式事务管理
3)基于@Transactional的声明式事务管理
4)基于Aspectj AOP配置事务

10、REDIS

redis的类型:String / List / Hash / sorted set / Set
持久化的方式:
1)RDB方式-将Redis在内存中的数据库定时dump到磁盘上
2)AOF方式-将redis的操作日志以追加的方式写入到文件中。

11、ThreadLocal

线程同步机制是多个线程共享同一个变量,而ThreadLocal是为每个线程创建一个单独的变量副本,每个线程都可以改变自己的变量副本而不影响其他线程所对应的副本。
方法:
1)get():返回此线程局部变量当前副本中的值
2)set(T value):将线程局部变量当前副本中的值设置为指定值
3)initialValue():返回此线程局部变量当前副本中的初始值
4)remove():移除此线程局部变量当前副本中的值

12、数据库连接池

1、数据库连接是一个费时的操作,连接池可以使多个操作共享一个连接
2、数据库连接池的基本思想就是为数据库连接建立一个缓冲池。预先在缓冲池中放入一定数量的连接,当需要建立数据库连接时,从缓冲池中取出一个,使用完毕后再放回去。可以通过设定连接池最大连接数来防止系统无尽的与数据库连接,更重要的是我们可以通过连接池的管理机制监视数据库的连接的数量、使用情况,为系统开发,测试及性能调整提供依据。

13、线程池

创建线程池要花费昂贵的资源和时间,如果任务来了才创建线程那么响应时间时间会变长,而且一个进程能创建的线程数有限;
为了避免这些问题,在程序启动的时候就创建若干线程来响应处理,它们被称为线程池,里面的线程叫工作线程;
从JDK1.5开始,Java API提供了Executor框架让你可以创建不同的线程池,比如单线程池,每次处理一个任务;数目固定的线程池或者是缓存线程池。

14、多线程

volatile

变量声明为volatile,就以为着这个变量是随时会被其他线程修改的,因此不能将它cache在线程memory中。

线程生命周期

新建一个线程时,状态为new;调用线程的start()方法时,状态为Runnable;线程调度器为Runnable线程池中的线程分配CPU时间并且将他们的状态改变为Running;其他的线程状态还有Waiting,Blocked和Dead.

死锁

指两个以上的线程永远阻塞的情况,这种情况产生至少需要两个以上的线程和两个以上的资源。

Timer类

工具类,可用于安排一个线程在未来的某个特定时间执行,Timer类可以用安排一次性任务或者周期任务。

同步集合和并发集合

都为多线程和并发提供了合适的线程安全的集合,并发集合的可扩展性更高
ConcurrentHashMap提供线程安全还用锁分离和内部分区等现代技术提高了可扩展性。

15、集合

Collection:集合的根接口

Set:不能包含重复元素的集合,这个接口对数据集合抽象进行建模
List:是一个有序集合,可以包含重复元素,可以通过索引来访问任何元素,更像长度动态变换的数组。
Map:将key映射到value的对象,一个Map不能包含重复的key,每个key最多只能映射一个value.
数组可以一开始不定义长度,但要进行初始化时就必须得定义长度。

Enumeration/iterator

enum的速度是iterator的两倍,使用更好的内存;
iterator更加安全,当一个集合被遍历时时阻止其他线程去修改集合。

数组的排序

数组:Arrays.sort();
对象列表:Collection.sort();

java集合框架的实践

1、根据需要选择正确的集合类型。如果指定了大小,选用Array非ArrayList;如果想根据插入顺序遍历一个Map,使用TreeMap;如果不想重复,使用set
2、基于接口编程,而非基于实现编程,它允许我们后来轻易地改变实现。
3、总是使用类型安全的泛型,避免在运行时出现ClassCastException;
4、使用JDK提供的不可变类作为Map的key,可以避免自己实现hashCode()和equals();
5、尽可能使用Collections工具类,或者获取只读、同步或空的集合,而非编写自己的实现,它将会提供代码重用性,有桌更好的稳定性和可维护性。

16、动态代理

要点:
* 1、动态基类-即普通类的抽象父类
* 2、继承了抽象父类的普通类,具体方法处理
* 3、增强类-实现了InvocationHandler接口的类
* 1)定义 Ojbect类型的对象属性;
* 2)在构造函数中注入要被增强的类,此代表继承了抽象父类的普通类
* 3)重写invoke()方法,做具体的增强操作
* 4、实际项目程序调用

17、设计模式

1、工厂模式:普通工厂、多个工厂方法模式、静态工厂模式

就是建立一个工厂类,对实现了统一接口的一些类进行实例化的创建

1)静态工厂
    public class SendFactory{
       public static Sender produceMail(){
         return new MailSender();
       }
       public static Sender produceSms(){
         return new SmsSender();
       }
    }
2、抽象工厂模式
1)public inteface Sender{ public void send(); }//抽象类

2)public class MailSender implements Sender{//抽象实现类
  public void Send(){ System.out.println("xxxx");}
}

3)public class SmsSender implements Sender{//抽象实现类
  public void Send(){ System.out.println("yyyy");}
}

4)public interface Provider{ public Sender produce();}

5)public class SendMailFactory implements Provider{//工厂类
  public Sender produce(){ return new MailSender();}
}

6)public class SendSmsFactory implements Provider{//工厂类
  public Sender produce(){ return new SmsSender();}
}
3、单例模式
 public class Singlton{
   //防止对象的实例化
   private Singleton(){}
   //内部类维护实例的创建
   private static class SingletonFactory{
     private static Singleton instance = new Singleton();
   }
   public static Singleton getInstance(){
     return SingletonFactory.instance;
   }
   //如果该对象呗用于序列化,可以保证对象在序列化前后保持一致
   public Object readResolve(){
     return getInstance();
   }
 }
4、建造者模式
  将各种产品集中起来进行管理,用来创建复合对象(与工厂模式类似-单)
  public class Builder{
    private List<Sender> list = new ArrayList<Sender>();
    public void produceMailSender(int count){
       for(int i=0;i<ocunt;i++){
         list.add(new MailSender());
       }
    }
    public void produceSmsSender(int count){
       for(int i=0;i<count;i++){
         list.add(new SmsSender());
       }
    }
  }
5、适配器模式
   案例背景:source类有method1()方法;
            Targetable为接口,有method1和method2两个方法
        目的:Targetable接口的实现类具有Source类的功能。
   public class Wrapper implements Targetable{
     private Source source;

     public Wrapper(Source source){
      super();
      this.source = source;
     }

     public void method2(){xxxxx}
     public void method1(){source.method1();}
   }
6、装饰者模式

给一个对象增加一些新的功能,而且是动态的,要求装饰对象和被装饰对象实现同一个接口,装饰对象持有被装饰对象的实例

1)public interface Sourceable{ public void method();}
2)public class Source implements Sourceable{
      public void method(){ System.out.println("xxxx");}
  }
3)public class Decorator implements Sourceable{
      private Sourceable source;
      public Decorator(Sourceable source){
           super();
           this.source = source;
      }
      public void method(){
           System.out.println("扩展程序");
           source.method();
      }
  }

18、NIO

主要核心:channel/buffer/selector

1、使用Buffer读写数据一般遵循四个步骤

1)写入数据到Buffer;
2)调用flip()方法;
3)从Buffer中读取数据;
4)调用clear()方法或者compact()方法。

2、channel通过此对象读取和写入数据

1)channel是双向的,既可以写又可以读;
2)channel可以进行异步的读写;
3)对channel的读写必须通过buffer对象。

3、案例

1)从文件读取数据

 FileInputStream fin = new FileINputStream("addr");
 FileChannel fc = fin.getChannel();
 ByteBuffer buffer = ByteBuffer.allocate(1024);
 fc.read(buffer);

2)写入数据到文件

 FileOutputStream fout = new FileOutputStream("addr");
 FileChannel fc = fout.getChannel();
 ByteBuffer buffer = ByteBuffer.allocate(1024);
 for(int i=0;i<message.length;++i){
   buffer.put(message[i]);
 }
 buffer.flip();
 fc.write(buffer);

3)读写结合

 FileInputStream fi = new FileInputStream("addr");
 FileOutputStream fout = new FileOutputStream("addr");
  FileChannel fic = fi.getChannel();
  FileChannel foc = fout.getChannel();
  ByteBuffer buffer = ByteBuffer.allocate(1024);
  while(true){
    int eof = fic.read(buffer);
    if(eof == -1){
      break;
    }
    buffer.flip();
    foc.write(buffer);
    buffer.clear();
  }
  fic.close();
  foc.close();
  fi.close();
  fo.close();

19、mybatis缓存

查询时首先去缓存中查询结果集,如果没有则查询数据库,有则直接返回;mybatis内部存储缓存使用一个hashmap,key为hashCode+sqlId+sql语句,value为从查询出来映射生成的结果集
mybatis的二级缓存即查询缓存,他的作用域是一个mapper的namespace,即在同一个namespace中查询sql可以从缓存中获取数据,二级缓存是可以跨SqlSession的

20、Hibernate

1)sessionFactory

线程不安全,对应hibernate中数据存储的概念,可以被多个线程并发访问,单例模式为佳

2)session

轻量级非线程安全的对象,线程间不共享;由sessionFactory创建,是持久层服务对外提供的主要接口
session会延迟获取数据库连接,在需要时才获取;为避免创建太多的session,可以使用ThreadLocal将session和当前线程绑定在一起,保证同一个线程获得的总是同一个session

3)对象的状态

瞬时态、游离态、持久态

4)session加载实体对象的过程

1、在调用数据库查询功能之前,首先会在一级缓存中通过实体类型和主键进行查找,如果查找命中且数据状态合法,则直接返回;
2、如果一级缓存未命中,则session会在当前NonExists记录中查找,如果存在同样的查询条件,则返回null
3、如果一级缓存查询失败,查询二级缓存,如果命中直接返回
4、如果没有命中,则发出sql语句,如果无对应记录则将此此查询添加到session的NonExists中加以记录,并返回null
5、根据映射配置和sql语句得到resultSet,并创建对应的实体对象
6、将对象纳入session的管理
7、如果有对应的拦截器,则执行拦截器的onLoad方法
8、如果开启并设置了要使用二级缓存,则将数据对象纳入二级缓存
9、返回数据对象

21、spring

1)好处:

轻量:基本版本约2MB
控制反转:实现了松散耦合,对象们给出他们的依赖,而不是创建或查找依赖的对象们
切面编程:把应用业务逻辑和系统服务分开
容器:包含并管理应用中对象的生命周期和配置
MVC框架:精心设计的web的框架
事务管理:提供了事务管理接口,可以扩展到上至本地事务下至全局事务
异常处理:提供方便的API把具体技术相关的异常转化为一直的unchecked异常

2)依赖注入?有哪几种方式?

是指不用创建对象,而只需要描述它如何被创建。不在代码里 直接组装你的组件和服务,但要在配置文件里描述哪些组件需要哪些服务,之后IOC容器负责把相关的组件组装起来
方式:构造器依赖注入、setter方法注入

3)spring beans

是指形成spring应用的主干的java对象,他们被spring ioc容器初始化、装配和管理。这些beans通过容器中配置的元数据创建。

4)spring中的bean的生命周期

spring容器从xml文件中读取bean的定义并实例化bean;
spring根据bean的定义填充相关属性;。/.。

5)spring中如何注入一个java集合
可以通过<list>/<set>/<map>/<props>注入元素

22、JDBC

1)访问数据库的步骤

加载驱动;//Class.forName(driver);
通过DriverManager对象获取连接对象Connection//Connection con = DriverManager.getConnection(url,user,password);
通过连接对象获取会话//Statement st = con.createStatement();
通过会话进行数据的增删改查,封装对象st.executeQuery(sql);
关闭资源

2) 事务

ACID 原子性、一致性、隔离性、持久性
步骤:con.setAutoCommit(false);//设置提交方式为手动提交
con.commit();//提交事务
con.rollback();异常回滚

脏读:有一行数据刚更新,同时另一个查询读到这个数据,导致脏读,因为这个更新的数据还未进行持久化,有可能进行回滚操作。
幻读:指一个事务多次执行一条查询返回的却是不同的值,即一个事务正根据某个条件进行数据查询,然后另一个事务插入了一行满足查询条件的数据,之后这个事务再次执行查询查到刚插入的新数据,这行新数据成为幻行,现象成为幻读。

23、mysql数据库

数据库的三范式

1)数据库表的每一个字段都是不可分割的;
2)数据库表中的非主属性只依赖于主键
3)不存在非主属性对关键字的传递函数依赖关系

1)主从同步复制时丢包问题怎么办?

使用半同步复制,在5.5版本开始支持;
主库配置sync_binlog=1,innodb_flush_log_at_trx_commit=1
从库问题:优化网络;升级slave硬件配置;slave调整参数;升级mysql版本5.7,使用并行复制

2)数据库优化
1、数据库设计和表创建时就要考虑性能

表字段避免null值出现,null值很难查询优化且占用额外的索引空间,推荐默认数字0代替null;
尽量使用INT而非BIGINT,如果非负则加上UNSIGNED(这样数值容量会扩大一倍),当然能使用TINYINT、SMALLINT、MEDIUM_INT更好;
使用枚举或整数代替字符串类型;
尽量使用TIMESTAMP而非DATETIME;
单表不要有太多字段,建议在20以内;
用整型来存IP。

2、索引

索引并不是越多越好,要根据查询有针对性的创建,考虑在WHERE和ORDER BY命令上涉及的列建立索引,可根据EXPLAIN来查看是否用了索引还是全表扫描
应尽量避免在WHERE子句中对字段进行NULL值判断,否则将导致引擎放弃使用索引而进行全表扫描
值分布很稀少的字段不适合建索引,例如"性别"这种只有两三个值的字段
字符字段只建前缀索引
字符字段最好不要做主键
不用外键,由程序保证约束
尽量不用UNIQUE,由程序保证约束
使用多列索引时注意顺序和查询条件保持一致,同时删除不必要的单列索引

3、sql的编写需要注意优化

使用limit对查询结果的记录进行限定
避免select *,将需要查找的字段列出来
使用连接(join)来代替子查询
拆分大的delete或insert语句
可通过开启慢查询日志来找出较慢的SQL
不做列运算:SELECT id WHERE age + 1 = 10,任何对列的操作都将导致表扫描,它包括数据库教程函数、计算表达式等等,查询时要尽可能将操作移至等号右边
sql语句尽可能简单:一条sql只能在一个cpu运算;大语句拆小语句,减少锁时间;一条大sql可以堵死整个库
OR改写成IN:OR的效率是n级别,IN的效率是log(n)级别,in的个数建议控制在200以内
不用函数和触发器,在应用程序实现
避免%xxx式查询
少用JOIN
使用同类型进行比较,比如用’123’和’123’比,123和123比
尽量避免在WHERE子句中使用!=或<>操作符,否则将引擎放弃使用索引而进行全表扫描
对于连续数值,使用BETWEEN不用IN:SELECT id FROM t WHERE num BETWEEN 1 AND 5
列表数据不要拿全表,要使用LIMIT来分页,每页数量也不要太大

3)存储引擎

1)InnoDB:默认事务型引擎,支持事务安全表、行锁定和外键
处理巨大数据量的最大性能设计
在主内存中缓存数据和索引而维持它自己的缓冲池
支持外键完整性约束,如果没有显示指定主键,会为每一行生成一个6字节的ROWID,并作为主键
场景:对事务的完整性要求比较高,要求实现并发控制,频繁更新、删除操作

2)MyISAM:web、数据仓储和其他应用环境下最长使用的存储引擎,较高的插入、查询速度,但不支持事务和外键
大文件
表的最大索引是64,最大列数是16
最大的键长度是1000字节
BLOB和TEXT列可以被索引

4)数据库中字段varchar和char的区别

1)char:定长,固定长度,不足长度用空格填满,最大存储容量255个字符
2)varchar:变长,长度变化,最大存储容量65532个字符

5)事务的隔离级别

1)Read uncommited
读未提交,即一个事务可以读取另一个未提交事务的数据,会出现脏读问题
解决脏读:Read commited.读提交,能解决脏读问题

2)Read commited
读提交,即一个事务要等另一个事务提交后才能读取数据,会出现不可重复读
解决不可重复读:Repeatable read

3)Repeatable read(mysql默认支持级别)
重复读,就是在开始读取数据(事务开启)时,不再允许修改操作

4)什么时候会出现幻读
解决幻读问题:Serializable

5)Serializable:最高的事务隔离级别,在此级别下事务串行化顺序执行,可以避免脏读、不可重复读、与幻读;但效率低下,比较耗数据库性能.

24、springcloud

  1. eureka注册中心-与zookeeper类似:项目将自己注册到注册中心去,由消费者在注册中心上找相关的服务提供者并消费;
    2)ribbon 负载均衡;以及feign;
  2. zuul和nginx类似,都可实现负载均衡、反向代理,过滤请求,实现网关的效果。
    由java语言编写,采用ribbon和eureka实现本地负载均衡

25、nginx

1) 反向代理,屏蔽服务器真实路径
2)静态资源服务器,可以将静态文件html/js等放到nginx中,减轻tomcat服务器的压力
3)作负载均衡使用,可以在nginx中配置多个tomcat服务器,不同浏览器输入地址访问不同的tomcat.
4)访问控制
5)日志配置
6)防盗链

正向代理:对服务器屏蔽真实用户,如vpn
反向代理:对用户屏蔽真实服务器路径,用户访问统一到某个路径,再由此路径转发到不同的服务器中去。

26、webservice

服务端发布接口代码,客户端请求接收
1)客户端请求接口编译相关文件:wsimport -s . address
2)把编译好的包及其java文件放入项目中,
3)在项目中调用

普通webservice与restful风格的webservice的区别
RESTFul简化了webservice的设计,不再需要wsdl,也不需要soap,只需要通过最简单的http协议传输数据,既简化设计,也简化网络传输量
普通的webservice传输需要额外的xml封装

27、IO流

1、分类

1)流向:输入流、输出流
2)数据类型:字节流InputStream/OutputStream、字符流writer/reader

2、ObjectInputStream/FileInputStream的子类是装饰流(装饰器模式的主角)
常用案例
1)使用FileInputStream类读取文件内容
public class firstIO{
     public static void main(String[] args) throws IOException{
       int i=0;
       FileInputStream in = null;
       try{
         in = new FileInputStream("address");
       }catch(FileNotFoundException e){
          system.out.println("找不到文件位置");
       }
       try{
         int num =0;
         /**
         *优化部分:此处可定义一个字节数组,然后一次读取一个数组的量,优化效率
         *byte[] b = new byte[1024];
         *while((i = in.read(b)) != -1)
         */
         while((i=in.read())!=-1){
           System.out.println((char) i);//将得到的ASCII码值转换成字符型
           num++;
         }
         in.close();
         System.out.println("转换字节个数:" + num);
       }catch(Exception e){
          System.out.println(读取文件错误);
       }
     }
  }
2)使用FileOutputStream写入文件
public class outIO{
    public static void main(String[] args){
      int i=0;
      FileInputStream in = null;
      FileOutputStream out = null;
      try{
        int = new FileInputStream("address");
        out = new FileOutputStream("address");
        while((i = in.read()) != -1){
          out.write(i);
          System.out.println((char)i);
        }
        in.close();
        out.close();
      }catch(Exception e){
        System.out.println("复制出错");
      }
    }
}
3)使用FileReader/FileWriter复制文件
public class FileReaderAndWriter{
     public static void main(String[] args){
       FileReader fr = null;
       FileWriter fi = null;
       int i = 0;
       try{
         fr = new FileReader("address");
         fi = new FileWriter("address");
         while((i = fr.read()) != -1){
          fi.write(i);
         }
         fr.close();
         fi.close();
       }
     }
}
4)BufferInputStream/BufferOutputStream//提高效率
  InputStream in = new InputStream("address");
  BufferedInputStream bis = new BufferedInputStream(in);
  while((i = bis.read(b)) != -1){}

28、Mybatis为什么只有mapper接口没有实现类

mybatis的mapper接口没有相应的实现类。怎么实现相关的功能?动态代理。

mybatis的动态代理过程

初始化SqlSessionFactory解析mapper.xml的namespace属性的时候,将MapperProxyFactory代理工厂存入mapper缓存中,源代码的调取过程如下:

org.mybatis.spring.SqlSessionFactoryBean#buildSqlSessionFactory ->
if (this.mapperLocations.length == 0) {
  LOGGER.warn(() -> "Property 'mapperLocations' was specified but matching resources are not found.");
} else {
  for (Resource mapperLocation : this.mapperLocations) {
    if (mapperLocation == null) {
      continue;
    }
    try {
      XMLMapperBuilder xmlMapperBuilder = new XMLMapperBuilder(mapperLocation.getInputStream(),
      targetConfiguration, mapperLocation.toString(), targetConfiguration.getSqlFragments());
      xmlMapperBuilder.parse();
    } catch (Exception e) {
      throw new NestedIOException("Failed to parse mapping resource: '" + mapperLocation + "'", e);
    } finally {
      ErrorContext.instance().reset();
    }
    LOGGER.debug(() -> "Parsed mapper file: '" + mapperLocation + "'");
  }
}

org.apache.ibatis.builder.xml.XMLMapperBuilder#parse ->
public void parse() {
  if (!configuration.isResourceLoaded(resource)) {
    configurationElement(parser.evalNode("/mapper"));
    configuration.addLoadedResource(resource);
    bindMapperForNamespace();
  }

  parsePendingResultMaps();
  parsePendingCacheRefs();
  parsePendingStatements();
}

org.apache.ibatis.builder.xml.XMLMapperBuilder#bindMapperForNamespace ->
private void bindMapperForNamespace() {
  String namespace = builderAssistant.getCurrentNamespace();
  if (namespace != null) {
    Class<?> boundType = null;
    try {
      boundType = Resources.classForName(namespace);
    } catch (ClassNotFoundException e) {
      //ignore, bound type is not required
    }
    if (boundType != null) {
      if (!configuration.hasMapper(boundType)) {
        configuration.addLoadedResource("namespace:" + namespace);
        configuration.addMapper(boundType);
      }
    }
  }
}

org.apache.ibatis.session.Configuration#addMapper ->
public <T> void addMapper(Class<T> type) {
  mapperRegistry.addMapper(type);
}

org.apache.ibatis.binding.MapperRegistry#addMapper ->
public <T> void addMapper(Class<T> type) {
  if (type.isInterface()) {
    if (hasMapper(type)) {
      throw new BindingException("Type " + type + " is already known to the MapperRegistry.");
    }
    boolean loadCompleted = false;
    try {
      knownMappers.put(type, new MapperProxyFactory<>(type));
      MapperAnnotationBuilder parser = new MapperAnnotationBuilder(config, type);
      parser.parse();
      loadCompleted = true;
    } finally {
      if (!loadCompleted) {
        knownMappers.remove(type);
      }
    }
  }
}

//注册bean(mapper)的时候会调用doGetObjectFromFactoryBean,这个时候FactoryBean<?>传入的是MapperFactoryBean对象,然后获取前面存入knownMappers里面的MapperProxyFactory代理工厂,用代理工创建一个Mapper代理实例给容器注册

org.springframework.beans.factory.support.FactoryBeanRegistrySupport#doGetObjectFromFactoryBean ->
private Object doGetObjectFromFactoryBean(final FactoryBean<?> factory, final String beanName)
throws BeanCreationException {

  Object object;
  try {
    if (System.getSecurityManager() != null) {
      AccessControlContext acc = getAccessControlContext();
      try {
        object = AccessController.doPrivileged((PrivilegedExceptionAction<Object>) factory::getObject, acc);
      }
      catch (PrivilegedActionException pae) {
        throw pae.getException();
      }
    } else {
      object = factory.getObject();
    }
  }
  catch (FactoryBeanNotInitializedException ex) {
    throw new BeanCurrentlyInCreationException(beanName, ex.toString());
  }
  catch (Throwable ex) {
    throw new BeanCreationException(beanName, "FactoryBean threw exception on object creation", ex);
  }

  if (object == null) {
    if (isSingletonCurrentlyInCreation(beanName)) {
      throw new BeanCurrentlyInCreationException(
        beanName, "FactoryBean which is currently in creation returned null from getObject");
    }
    object = new NullBean();
  }
  return object;
}

org.mybatis.spring.mapper.MapperFactoryBean#getObject ->
public T getObject() throws Exception {
  return getSqlSession().getMapper(this.mapperInterface);
}


org.mybatis.spring.SqlSessionTemplate#getMapper ->
public <T> T getMapper(Class<T> type) {
  return getConfiguration().getMapper(type, this);
}

org.apache.ibatis.session.Configuration#getMapper ->
public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
  return mapperRegistry.getMapper(type, sqlSession);
}

org.apache.ibatis.binding.MapperRegistry#getMapper ->
public <T> T getMapper(Class<T> type, SqlSession sqlSession) {
  final MapperProxyFactory<T> mapperProxyFactory = (MapperProxyFactory<T>) knownMappers.get(type);
  if (mapperProxyFactory == null) {
    throw new BindingException("Type " + type + " is not known to the MapperRegistry.");
  }
  try {
    return mapperProxyFactory.newInstance(sqlSession);
  } catch (Exception e) {
    throw new BindingException("Error getting mapper instance. Cause: " + e, e);
  }
}

所以,注册到spring容器的mapper其实是MapperProxy,我们调用mapper接口的时候就会自动装配动态生成的MapperProxy实例实现mapper的功能。

29、出现java.lang.outofMemoryError怎么排查

导致OutOfMemoryError异常的常见原因有以下几种:

1)内存中加载的数据量过于庞大,如一次从数据库取出过多数据;
2)集合类中有对对象的引用,使用完后未清空,使得JVM不能回收;
3)代码中存在死循环或循环产生过多重复的对象实体;
4)使用的第三方软件中的BUG;
5)启动参数内存值设定的过小;

解决方案
1)增加jvm的内存大小。

方法有: 1)在执行某个class文件时候,可以使用java -Xmx256M aa.class来设置运行aa.class时jvm所允许占用的最大内存为256M。 2)对tomcat容器,可以在启动时对jvm设置内存限度。对tomcat,可以在catalina.bat中添加:

set CATALINA_OPTS=-Xms128M -Xmx256M
set JAVA_OPTS=-Xms128M -Xmx256M

或者把%CATALINA_OPTS%和%JAVA_OPTS%代替为-Xms128M -Xmx256M。
3)对resin容器,同样可以在启动时对jvm设置内存限度。在bin文件夹下创建一个startup.bat文件,内容如下:
@echo off
call “httpd.exe” “-Xms128M” “-Xmx256M”
:end
其中"-Xms128M"为最小内存,"-Xmx256M"为最大内存。

2)优化程序,释放垃圾。

主要包括避免死循环,应该及时释放种资源:内存, 数据库的各种连接,防止一次载入太多的数据。导致java.lang.OutOfMemoryError的根本原因是程序不健壮。因此,从根本上解决Java内存溢出的唯一方法就是修改程序,及时地释放没用的对象,释放内存空间。 遇到该错误的时候要仔细检查程序,嘿嘿,遇多一次这种问题之后,以后写程序就会小心多了。

Java代码导致OutOfMemoryError错误的解决

需要重点排查以下几点:
1)检查代码中是否有死循环或递归调用。
2)检查是否有大循环重复产生新对象实体。
3)检查对数据库查询中,是否有一次获得全部数据的查询。一般来说,如4)果一次取十万条记录到内存,就可能引起内存溢出。这个问题比较隐蔽,在上线前,数据库中数据较少,不容易出问题,上线后,数据库中数据多了,一次查询就有可能引起内存溢出。因此对于数据库查询尽量采用分页的方式查询。
5)检查List、MAP等集合对象是否有使用完后,未清除的问题。List、MAP等集合对象会始终存有对对象的引用,使得这些对象不能被GC回收。

tomcat中java.lang.OutOfMemoryError: PermGen space异常处理

PermGen space的全称是Permanent Generation space,是指内存的永久保存区域,这块内存主要是被JVM存放Class和Meta信息的,Class在被Loader时就会被放到PermGen space中, 它和存放类实例(Instance)的Heap区域不同,GC(Garbage Collection)不会在主程序运行期对PermGen space进行清理,所以如果你的应用中有很多CLASS的话,就很可能出现PermGen space错误, 这种错误常见在web服务器对JSP进行pre compile的时候。如果你的WEB APP下都用了大量的第三方jar, 其大小超过了jvm默认的大小(4M)那么就会产生此错误信息了。 解决方法: 手动设置MaxPermSize大小修改

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值