java学习笔记

来源:互联网 发布:tomcat 域名绑定 路由 编辑:程序博客网 时间:2024/04/27 18:00

1、数据库

SELECT * FROM Biz.TB_USER user left join Biz.TB_INVEST invest on user.ID=invest.USERID where user.ID = invest.USERID;

首先:left join ...on user.ID = invest.ID,我们要明确,left join是肯定包含Biz.TB_USER中的所有记录,这个on的作用是选出Biz.TB_INVEST中符合这个user.ID = invest.ID条件的记录,并将这些记录放在Biz.TB_USER记录的左边,若无匹配,则全部用null来表示。而where user.ID = invest.USERID这个是真正的筛选条件,它会把不符合这个条件的记录都去掉。优化建议:使用left join时,我们尽量在on后面的条件中将不需要的记录过滤掉,不要留到where子句中才过滤。即:尽可能满足on的条件,而少用where的条件。

例如:

select * from A inner join B on B.name = A.name left join C on C.name = B.name left join D on D.id = C.id where C.status>1 and D.status=1;

改成:

select * from A inner join B on B.name = A.name left join C on C.name = B.name and C.status>1 left join D on D.id = C.id and D.status=1;

这样性能更佳。


2、

###sql注入:

在用户名输入框中输入:' or 1=1#,密码随便输入,这时候的合成后的SQL查询语句为: 

select * from users where username='' or 1=1#' and password=md5('') 

语义分析:“#”在MySQL中是注释符,这样井号后面的内容将被mysql视为注释内容,这样就不会去执行了,换句话说,以下的两句sql语句等价: 

select * from users where username='' or 1=1#' and password=md5('') 

等价于 

select * from users where username='' or 1=1 

###跨站脚本攻击:

通过构建一个链接,诱惑受害者点击,从而得到受害者的一些信息。如何防御:对请求的字段进行过滤;

###跨站请求伪造:CSRF:

如何预防:根据HTTP协议,在HTTP头中有一个字段叫Referer,它记录了该HTTP请求的来源地址,校验此信息;


3、java基础:字符串常量池

 String s1 = new StringBuilder("go").append("od").toString(); System.out.println(s1.intern() == s1); //true String s2 = new StringBuilder("ja") .append("va").toString(); System.out.println(s2.intern() == s2); //false


4、java关键字transient

java 的transient关键字为我们提供了便利,你只需要实现Serilizable接口,将不需要序列化的属性前添加关键字transient,序列化对象的时候,这个属性就不会序列化到指定的目的地中:

1)一旦变量被transient修饰,变量将不再是对象持久化的一部分,该变量内容在序列化后无法获得访问。

2)transient关键字只能修饰变量,而不能修饰方法和类。注意,本地变量是不能被transient关键字修饰的。变量如果是用户自定义类变量,则该类需要实现Serializable接口。

3)被transient关键字修饰的变量不再能被序列化,一个静态变量不管是否被transient修饰,均不能被序列化。

4)  如果一个变量是static的,那么无论是否有transient修饰词,都不能够别序列化,因为序列化是序列化一个对象,static变量根本不属于任何对象。


5、HashMap工作原理

  1)存储对象时,我们将K/V传给put方法时,它调用hashCode计算hash从而得到bucket位置,进一步存储,HashMap会根据当前bucket的占用情况自动调整容量(超过Load Factor则resize为原来的2倍)。如果发生碰撞的时候,Hashmap通过链表将产生碰撞冲突的元素组织起来。如果一个bucket中碰撞冲突的元素超过某个限制(默认是8),则使用红黑树来替换链表,从而提高速度。

  2)获取对象时,我们将K传给get,它调用hashCode计算hash从而得到bucket位置,并进一步调用equals()方法确定键值对。

HashMap 中可以保存键为NULL的键值对,且该键值对是唯一的。若再次向其中添加键为NULL的键值对,将覆盖其原值。此外,如果HashMap中存在键为NULL的键值对,那么一定在第一个桶中,即table[0]位置。

随着HashMap中元素的数量越来越多,发生碰撞的概率就越来越大,所产生的链表长度就会越来越长,这样势必会影响HashMap的速度,为了保证HashMap的效率,系统必须要在某个临界点进行扩容处理。该临界点在当HashMap中元素的数量等于table数组长度*加载因子。但是扩容是一个非常耗时的过程,因为它需要重新计算这些数据在新table数组中的位置并进行复制处理。所以如果我们已经预知HashMap中元素的个数,那么预设元素的个数能够有效的提高HashMap的性能。每次扩展两倍(数组的大小扩展2倍)

哈希的应用:数据结构

我们知道,数组的特点是:寻址容易,插入和删除困难;而链表的特点是:寻址困难,插入和删除容易。那么我们能不能综合两者的特性,做出一种寻址容易,插入和删除也容易的数据结构呢?答案是肯定的,这就是我们要提起的哈希表。事实上,哈希表有多种不同的实现方法,我们接下来解释的是最经典的一种方法 —— 拉链法,我们可以将其理解为 链表的数组,如下图所示:



6、说说&和&&的区别。

&和&&都可以用作逻辑与的运算符,

表示逻辑与(and),当运算符两边的表达式的结果都为true时,整个运算结果才为true,否则,只要有一方为false,则结果为false。

&&还具有短路的功能,即如果第一个表达式为false,则不再计算第二个表达式,例如,对于if(str != null&& !str.equals(“”))表达式,当str为null时,后面的表达式不会执行,所以不会出现NullPointerException如果将&&改为&,则会抛出NullPointerException异常。If(x==33 &++y>0) y会增长,If(x==33 && ++y>0)不会增长。

&还可以用作位运算符,当&操作符两边的表达式不是boolean类型时,&表示按位与操作,我们通常使用0x0f来与一个整数进行&运算,来获取该整数的最低4个bit位,例如,0x31 & 0x0f的结果为0x01。


7、使用java提供的类进行排序,很实用

package CollectionTest;import java.awt.Color;import java.util.Objects;public class Apple implements Comparable {    private String variety;    private Color color;    private Integer weight;        public Apple(String variety, Color color,int weight){        this.variety = variety;        this.color = color;        this.weight = weight;            }    @Override    public int compareTo(Object other) {                Apple apple = (Apple) other;                if (this.weight < apple.weight) {            return -1;        }        if (Objects.equals(this.weight, apple.weight)) {            return 0;        }        return 1;    }        public String getVariety() {        return variety;    }    public Color getColor() {        return color;    }    public Integer getWeight() {        return weight;    }}List list0 = new ArrayList();list0.add(new Apple("red", Color.RED,13));list0.add(new Apple("red", Color.RED,1));list0.add(new Apple("red", Color.RED,2));Collections.sort(list0);//或者这种方式,无需Apple类实现Comparable接口,这样方便,无强侵入性Collections.sort(list0, new Comparator<Apple>(){     @Override     public int compare(Apple o1, Apple o2) {         return o1.getWeight().compareTo(o2.getWeight());     } });

8、mysql主从复制

    mysql支持的复制类型:

(1):基于语句的复制:  在主服务器上执行的SQL语句,在从服务器上执行同样的语句。MySQL默认采用基于语句的复制,效率比较高。  

            一旦发现没法精确复制时,   会自动选着基于行的复制。    

(2):基于行的复制:把改变的内容复制过去,而不是把命令在从服务器上执行一遍. 从mysql5.0开始支持

(3):混合类型的复制: 默认采用基于语句的复制,一旦发现基于语句的无法精确的复制时,就会采用基于行的复制。


9、红黑树

红黑树是一种特殊的二叉树,主要用它存储有序的数据,提供高效的数据检索,时间复杂度为O(lgn),每个节点都有一个标识位表示颜色,红色或黑色,有如下5种特性:



1、每个节点要么红色,要么是黑色;

2、根节点一定是黑色的;

3、每个空叶子节点必须是黑色的;

4、如果一个节点是红色的,那么它的子节点必须是黑色的;

5、从一个节点到该节点的子孙节点的所有路径包含相同个数的黑色节点;

6、红黑树并不是标准平衡二叉树,它以性质 5 作为一种平衡方法,使自己的性能得到了提升。

只要满足以上6个特性的二叉树都是红黑树,当有新的节点加入时,有可能会破坏其中一些特性,需要通过左旋或右旋操作调整树结构,重新着色,使之重新满足所有特性。ConcurrentHashMap红黑树实现:在1.8的实现中,当一个链表中的元素达到8个时,会调用treeifyBin()方法把链表结构转化成红黑树结构,

红黑树节点结构:

class TreeNode<K,V> extends Node<K,V> {    TreeNode<K,V> parent;  // red-black tree links    TreeNode<K,V> left;    TreeNode<K,V> right;    TreeNode<K,V> prev;        // needed to unlink next upon deletion    boolean red;}

10、java异常结构


/*自定义异常*/    class ChushulingException extends Exception    {        public ChushulingException(String msg)        {            super(msg);        }    }       /*自定义异常 End*/        class Numbertest     {        public int shang(int x,int y) throws ChushulingException        {              if(y==0)            {                throw new ChushulingException("您输入的是"+y+",除数不能为0!");            }                    int m=x/y;            return m;        }    }class Rt001{        public static void main(String[]args){            Numbertest n=new Numbertest();                //捕获异常            try            {                System.out.println("商="+n.shang(1,-3));            } catch(ChushulingException yc) {                System.out.println(yc.getMessage());                yc.printStackTrace();            }  catch(Exception y)   {                System.out.println(y.getMessage());                y.printStackTrace();            }  finally  {             System.out.println("finally!”);        } ////finally不管发没发生异常都会被执行              }    }    


12、EJB3 

 EJB3~单例会话bean

@Singleton    public class HitCounter {        int count;        public void increment() { ++count; }         @Lock(LockType.READ)        public int getCount() { return count; }        public void reset() { count = 0; }    } 

单例会话bean:便于应用共享某些定义在单例会话bean中的数据。默认由容器来控制单例会话bean的并发访问

如果开发人员希望能够更加细粒度地控制并发性,那么应该把单例会话bean配置为使用bean托管的并发性,这其实很简单只需要在bean类上添加@Concurrency Management(ConcurrencyManagementtype.BEAN)注解就可以了。这时将禁用容器托管的并发性,@Lock注解就失去了作用。此时开发人员就可以决定使用合适的Java并发机制来确保数据安全了。


13、单例模式的高正确率写法

package zhuanshiPattern;public class Sinlenton {    //通过volatile关键字来确保安全    //加个volatile可以防止JVM指令重排序时导致的并非单例    private static volatile Sinlenton  sin;        private Sinlenton(){}        public static Sinlenton getSinlenton(){        if(sin==null){            synchronized(Sinlenton.class){                if(sin==null){                    sin = new Sinlenton();                    return sin;                }            }        }        return sin;    }}

volatile关键字提供了内存可见性和禁止内存重排序;在虚拟机内存中有主内存和工作内存的概念,每个cpu都有自己的工作内存,当读取一个普通变量时,优先读取工作内存的变量,如果工作内存中没有对应的变量,则从主内存中加载到工作内存,对工作内存的普通变量进行修改,不会立马同步到主内存,内存可见性保证了在多线程的场景下,保证了线程A对变量的修改,其它线程可以读到最新值。当对volatile修饰的变量进行写操作时,直接把最新值写到主内存中,并清空其它cpu工作内存中该变量所在的内存行数据,当对volatile修饰的变量进行读操作时,会读取主内存的数据。

在高并发的场景中,通过一个boolean类型的变量isopen,控制代码是否走促销逻辑,该如何实现?

public class ServerHandler {    private volatile isopen;    public void run() {        if (isopen) {           //促销逻辑        } else {          //正常逻辑        }    }    public void setIsopen(boolean isopen) {        this.isopen = isopen    }}
以上是volatile关键字使用的两种场景:double check和状态标记量。


14、final 只是表示引用(地址)不可以修改,例如对于集合对象声明为final指的是引用不能被更改,但是你可以向其中增加,删除或者改变内容。final在Java中是一个保留的关键字,可以声明成员变量、方法、类以及本地变量。一旦你将引用声明作final,你将不能改变这个引用了,编译器会检查代码,如果你试图将变量再次初始化的话,编译器会报编译错误。


15、多态

指向子类的父类引用由于向上转型了,它只能访问父类中拥有的方法和属性,而对于子类中存在而父类中不存在的方法,该引用是不能使用的,尽管是重载该方法。若子类重写了父类中的某些方法,在调用该些方法的时候,必定是使用子类中定义的这些方法(动态连接、动态调用)。 

Animal a = new Cat("老鼠"); //向上转型   父类Animal的引用指向了自己的子类Cat的对象         a.showAnimal();  //a可以访问Animal自己类中定义的方法showAnimal          a.showInfo();  //a应该是访问Animal自己类中定义的方法showInfo,但是由于子类Cat重写了showInfo,所以这里面调用的是子类Cat的showInfo方法       //a.showInfoCat();  //这句话直接报错,因为a是父类Animal的引用,它只能调用Animal自己的方法,或者Cat中重写了其父类Animal的方法showInfo          Cat cat = (Cat) a;  //向下转型,这个时候,cat这个引用,可以访问Cat类中的所有方法了,因为它就是一个Cat对象了。  cat.showInfo();  cat.showInfoCat();  cat.showAnimal();  //子类也可以直接调用父类的方法(前提是父类的showAnimal方法是public的,否则不能调用) 



16、Spring事务管理产生的意义

在Spring基于aop的声明性事务处理产生前,基于JTA的事务管理将事务管理与数据处理的逻辑混在一起,代码高度耦合;而基于EJB的CMT声明性事务管理,强制要求引入EJB容器的支持,也有很大的限制;Spring提供的就很不错。


17、JDK1.6或以上版本,性能因素不再是选择ReentrantLock的理由,因为已经对synchronized进行很大的优化,目前他们的性能差距很小;

使用ReentrantLock的理由变成:等待可中断、可实现公平锁、锁绑定多个条件(一个ReentrantLock对象可以同时绑定多个Condition对象)。


18、线程池原理

 private final ThreadPoolExecutor mThreadPool =  new ThreadPoolExecutor(5,  

                                                                                                     SIZE_MAX_POOL,  

                                                                                                     0L, 

                                                                                                     TimeUnit.MILLISECONDS, 

                                                                                                     new LinkedBlockingQueue<Runnable>());

一个线程池,初始化的时候,里面是没有线程的,一个阻塞队列,一开始里面也是没有任务的。

当来了一个任务时,在正方形中画了一个小圆圈,代表初始化了一个线程,如果再来一个任务,就再画一个圆圈,表示再初始化了一个线程,连续初始化5个线程之后,如果第6个任务过来了,这时会把第6个任务放到阻塞队列;现在线程池中不是有5个线程了么,如果其中一个线程空闲了,就会从阻塞队列中获取第6个任务,进行执行。那如果任务产生的速度比消费的速度快呢?如果线程池的5个线程都在running状态,那么任务就先保存在阻塞队列中;如果队列满了,怎么办?如果队列满了,我们不是设置了最大线程数是10么,而线程池中只有5个线程,这时会新建一个线程去执行不能保存到阻塞队列的任务,然后我又在正方形中画了5个圆圈。那如果线程池中的线程数达到10个了,阻塞队列也满了,怎么办?这种情况通过自定义reject函数去处理这里任务了...好的,那如果运行一段时间之后,阻塞队列中的任务也执行完了,线程池中的线程会怎么样?这个好像超过核心线程数的线程会在空闲一段时间内自动回收...好的,那这种情况在什么场景下会发生?秒杀这个场景。


19、Spring部分注解作用、区别

Spring中什么时候用@Service,什么时候用@Resource?

当你需要定义某个类为一个bean,则在这个类的类名前一行使用@Service("XXX"),就相当于讲这个类定义为一个bean,bean名称为XXX;

当需要在某个类中定义一个属性,并且该属性是一个已存在的bean,要为该属性赋值或注入时在该属性上一行使用@Resource(name="xxx"),相当于为该属性注入一个名称为xxx的bean。

Spring不但支持自己定义的@Autowired注解,还支持几个由JSR-250规范定义的注解,它们分别是@Resource、@PostConstruct以及@PreDestroy。

@Resource的作用相当于@Autowired,只不过@Autowired按byType自动注入,而@Resource默认按 byName自动注入罢了。

@Resource有两个属性是比较重要的,分是name和type,Spring将@Resource注解的name属性解析为bean的名字,而type属性则解析为bean的类型。所以如果使用name属性,   则使用byName的自动注入策略,而使用type属性时则使用byType自动注入策略。如果既不指定name也不指定type属性,这时将通过反射机制使用byName自动注入策略。

@Resource装配顺序

  1. 如果同时指定了name和type,则从Spring上下文中找到唯一匹配的bean进行装配,找不到则抛出异常

  2. 如果指定了name,则从上下文中查找名称(id)匹配的bean进行装配,找不到则抛出异常

  3. 如果指定了type,则从上下文中找到类型匹配的唯一bean进行装配,找不到或者找到多个,都会抛出异常

  4. 如果既没有指定name,又没有指定type,则自动按照byName方式进行装配;如果没有匹配,则回退为一个原始类型进行匹配,如果匹配则自动装配;

spring 2.5 中除了提供 @Component 注释外,还定义了几个拥有特殊语义的注释,它们分别是:@Repository、@Service 和 @Controller。

在目前的 Spring 版本中,这 3 个注释和 @Component 是等效的,但是从注释类的命名上,很容易看出这 3 个注释分别和持久层、业务层和控制层(Web 层)相对应。

虽然目前这3 个注释和 @Component 相比没有什么新意,但 Spring 将在以后的版本中为它们添加特殊的功能。

所以,如果 Web 应用程序采用了经典的三层分层结构的话,最好在持久层、业务层和控制层分别采用上述注解对分层中的类进行注释。

@Service用于标注业务层组件

@Controller用于标注控制层组件(如struts中的action)

@Repository用于标注数据访问组件,即DAO组件

@Component泛指组件,当组件不好归类的时候,我们可以使用这个注解进行标注。

@Autowired  @Qualifier("chinese")  private Man man;  
当接口存在两个实现类的时候必须使用@Qualifier指定注入哪个实现类,否则可以省略,只写@Autowired。如果是ejb里面呢,用下面这种注入方式:
@EJB(beanName="LoanProductServiceBean")LoanProductService loanProductService;
通过@EJB注解注入LoanProductService这个接口,如果LoanProductService有不止一种实现,那么需要通过beanName属性指定某一实现的名称。

声明事务注解:@Transactional 

@Transactional  public class TestServiceBean implements TestService {       private TestDao dao;       public void setDao(TestDao dao) {           this.dao = dao;       }       @Transactional(propagation =Propagation.NOT_SUPPORTED)      public List getAll() {           return null;      } }

事物传播行为介绍: 

  @Transactional(propagation=Propagation.REQUIRED) :如果有事务, 那么加入事务, 没有的话新建一个(默认情况下)

  @Transactional(propagation=Propagation.NOT_SUPPORTED) :容器不为这个方法开启事务

  @Transactional(propagation=Propagation.REQUIRES_NEW) :不管是否存在事务,都创建一个新的事务,原来的挂起,新的执行完毕,继续执行老的事务

  @Transactional(propagation=Propagation.MANDATORY) :必须在一个已有的事务中执行,否则抛出异常

  @Transactional(propagation=Propagation.NEVER) :必须在一个没有的事务中执行,否则抛出异常(与Propagation.MANDATORY相反)

  @Transactional(propagation=Propagation.SUPPORTS) :如果其他bean调用这个方法,在其他bean中声明事务,那就用事务.如果其他bean没有声明事务,那就不用事务.

事物超时设置:

  @Transactional(timeout=30) //默认是30秒

事务隔离级别:

  @Transactional(isolation = Isolation.READ_UNCOMMITTED):读取未提交数据(会出现脏读, 不可重复读) 基本不使用

  @Transactional(isolation = Isolation.READ_COMMITTED):读取已提交数据(会出现不可重复读和幻读)

  @Transactional(isolation = Isolation.REPEATABLE_READ):可重复读(会出现幻读)

  @Transactional(isolation = Isolation.SERIALIZABLE):串行化

注释:

1~可重复读的数据库锁情况:

事务在读取某数据的瞬间(就是开始读取的瞬间),必须先对其加 行级共享锁,直到事务结束才释放;事务在更新某数据的瞬间(就是发生更新的瞬间),必须先对其加 行级排他锁,直到事务结束才释放。


2~可序列化的数据库锁情况:

事务在读取数据时,必须先对其加 表级共享锁 ,直到事务结束才释放;
事务在更新数据时,必须先对其加 表级排他锁 ,直到事务结束才释放。


20、gc roots

GC Roots可以是: 1.JAVA虚拟机栈中的本地变量引用对象; 2.方法区中静态变量引用的对象; 3.方法区中常量引用的对象; 4.本地方法栈中JNI引用的对象; 选择这些对象的依据是什么,为什么他们可以作为GC roots?因为:GC管理的主要区域是Java堆,一般情况下只针对堆进行垃圾回收。方法区、栈和本地方法区不被GC所管理,因而选择这些区域内的对象作为GC roots,被GC roots引用的对象不被GC回收。类实例创建是在方法中完成的,类的实例就是方法的局部变量。在方法被调用时会分配栈帧,局部变量的引用存放在栈帧中,在方法返回时栈帧被回收,局部变量的引用也被回收。栈中的引用是GC Roots的一种,被其指向的对象不会被回收。


11、jvm  gc两种形式

  • 当年轻代内存满时,会引发一次普通GC,该GC仅回收年轻代。需要强调的时,年轻代满是指Eden代满,Survivor满不会引发GC
  • 当年老代满时会引发Full GC,Full GC将会同时回收年轻代、年老代
  • 当永久代满时也会引发Full GC,会导致Class、Method元信息的卸载


2 0