Java 面试前的基础准备

使用这个在线网页编辑真的是不习惯,还是 windows live writer 好。

下面列一个清单用于最近的面试:( 清单是网上down的 )

  1. static,final,transient 等关键字的作用
  2. foreach 实现原理
  3. synchronized 和 volatile关键字
  4. List,Map,Set 及其各种实现类,底层实现原理,实现类的优缺点
  5. 设计模式(单例【懒汉,饿汉,静态代码块,线程安全不安全】,工厂,观察,包装)
  6. 多线程(Runable 和 Thread)
  7. 什么是线程池, 以及线程池的优点
  8. sleep() 和 wait() 的区别
  9. IO(字节,字符,序列,内存,对象,打印。。。)
  10. 反射机制和内省机制
  11. 解析 xml文件几种方式的原理与特点 [ Dom, SAX, PULL ]
  12. sql 和 nosql(mysql 和 redis)
  13. 数据结构和算法(栈,队列,链表,树,图,排序,搜索)
  14. JDK 源码
  15. JVM(GC 算法,垃圾回收器,类加载器,双委派类型,Java 内存模型,happens-before 原则)
  16. web 分布式 session 实现的方式(4种)
  17. SOA,RPC,dubbo
  18. 再添加几个快烂掉的点:override 和 overload,final 和 finally,

static 关键字的作用:

  1. 作用在类上(该类必须只能是内部类,访问内部类的成员时,可以直接内部类名点调用)
  2. 作用在方法上(可以通过类名点调用,但是不能修饰构造方法,因为构造方法本身就是静态方法
  3. 作用在代码块上(类被加载时,代码块自动执行,并且只执行一次,一般用于加载驱动或者单例设计模式
  4. 静态导包(使被导包内的静态成员可以直接在我们直接的类中直接使用,不需要带类名,比如 Math 类的 PI 常量,但缺点很明显,可读性性降低)

final 关键字的作用:

  1. 作用在类上 (该类变为最终类,不能被继承)
  2. 作用在属性上 (属性变为常量,常量的地址不允许被修改)
  3. 作用在方法上 (方法变为最终方法,不能被子类重写)
  4. 作用在形参列表上(在方法类,值类型不允许被修改,引用类型对象的属性可以被修改,但是引用类型对象不能被改变)

对于 final 的第二点,要记住这句话,使用  final 关键字修饰一个值类型变量时,值类型的值不允许修改;修饰的是引用型变量时,是指引用变量地址不能变,但是引用变量所指向的对象中的内容还是可以改变的。归结到一点就是常量的地址不能改。

transient 关键字的作用:

  1.  

foreach 实现原理:

    foreach就是 java 中的加强for循环,语法: for(Type agr : Coll){ }

    其实现原理就是使用了迭代器,所以会比普通 for 循环会快速一点,但是也有弊端,就是只能遍历元素,不能修改元素。

volatile 关键字作用:

单链集合中的知识点:

单链集合的根接口是Collection,其下有两个子接口 List 和 Set,前者能存储重复元素,后者不能存储重复元素。

1. List 集合特点:

         元素的存取顺序是一致的,也可以存储重复元素

     List 集合实现类的选择原则 : 查询多就选择ArrayList, 增删多就选择LinkedList, 如果都多就选择ArrayList。

     List 集合实现类中使用的 contains()和remove()方法底层依赖的equals()方法,如果要使用这些方法,存入的实体类要实现equals()方法,

     可以使用LinkedList 来模拟站和队列,需要使用的是其addFast(),addLast() 等方法,

     三种循环: 普通for ,迭代器, 增强for,

  • 普通 for 循环能删除集合元素(记住要及时将循环变量减一)
  • 迭代器,能删除集合元素(只能使用迭代器自身的 remove 方法)
  • 增强 for 循环,不能删除集合元素,只能用来遍历集合(普通数组也可以遍历,而且比普通for快)

之前写过的List集合的博客:ArrayList 去除重复元素

                                         【List 实现非递归删除目录

2. Set 集合的特点:

       元素的存取顺序不一定是一致的 ( Set集合在底层进行了排序,Hash算法去重并排序或者二叉树排序,不能保证排好后的顺序的存储的顺序一定不相同 ),不能存储重复元素

   Set 集合常用实现类: HashSet,TreeSet,LinkedHashSet

   HashSet 底层去重依赖的是hashCode()方法和equals()方法,为了减少调用equals()方法的频率,一定要重写hashCode()方法,因为只有当要存储的元素的 hash 值出现相同时才会调用equals()方法进行判重。

  LinkedHashSet 能保证元素的存取一致,因为有链表结构,又因为有hash算法,就能去重。

  TreeSet的特点:

          可以指定顺序,然后对象的存储会按照指定的顺序进行排序。

  实现排序的两种方式:

       1 . 元素实体类实现 Comparable 接口,并实现 compareTo() 方法,最好还要重写 equals() 方法,

       2 . 写一个 Comparator ( 比较器接口 ) 的实现类,并实现 compare() 方法,在创建 TreeSet 集合时,将比较器对象传递进去,( 也可以不写实现类,直接写个匿名内部类 )。然后 TreeSet 集合对象调用 add() 方法时就会自动进行比较(第一种方法的话,会自动将实体类对象提升为 Comparable 类型调用重写后的 compareTo方法 ,

      第二种方法的话就会调用比较器类的compare方法)compareTo方法和compare()是十分类似的,不同的就是前者有被添加的元素调用,参数只有一个,后者有两个参数,第一个参数是要添加的元素,第二个参数是已经添加的元素,(当然,这两种都会去遍历集合,直到给这个元素找到一个恰当的位置,最坏的情况就是要遍历整个集合才找到)

之前写过的Set集合的博客:HashSet 存储自定义对象如何保证元素唯一性

                                   【TreeSet 如何保证元素的唯一性

                                   【LinkedHashSet 的概述和使用

双链集合中的知识点:

双链集合的根接口是 Map ,Map中存储的是 key-value 对,其中key是唯一的,和 Set 集合一样,只是Map的唯一性针对于 key 。
  1.HashMap 和 Hashtable 的区别:

      仔细看这两个名称,会发现 Hashtable 不太符合驼峰式命名,那是因为他是比较的老的版本,Hashtable 出自 JDK1.0 版本,是线程安全的(导致效率低),HashMap 是 JDK1.2 版本出现的,是线程不安全的(所以效率高),这样一来 HashMap 就替代了 Hashtable了。(这点可以和 StringBuffer 一样,然后出来了 StringBuild 替代了StirngBuffer )。

还有重要的一点,就是 HashMap 允许 key 为 null键和null值,Hashtable 是不允许。

之前写的 Map 集合的博客:Map 集合概述及其特点

解析xml文件的三种方式: [ DOM, SAX, PULL]

DOM:消耗内存:先把 xml文档都读到内存中,然后再用 DOM API 来访问树形结构,并获取数据。这个写起来很简单,但是很消耗内存。
SAX:解析效率高,占用内存少,基于事件驱动的:更加简单地说就是对文档进行顺序扫描,当扫描到文档 (document)开始与结束、元素 (element) 开始与结束、文档 (document) 结束等地方时通知事件处理函数,由事件处理函数做相应动作,然后继续同样的扫描,直至文档结束。
PULL:与 SAX 类似,也是基于事件驱动,我们可以调用它的 next() 方法,来获取下一个解析事件(就是开始文档,结束文档,开始标签,结束标签),当处于某个元素时可以调用 XmlPullParser的getAttributte() 方法来获取属性的值,也可调用它的 nextText() 获取本节点的值。

什么是线程池, 以及线程池的优点:

线程池的基本思想是一种对象池的思想,开辟一块内存空间,里面存放了众多 (未死亡) 的线程,池中线程执行调度由池管理器来处理。当有线程任务时,从池中取一个,执行完成后线程对象归池,这样可以避免反复创建线程对象所带来的性能开销,节省了系统的资源。就好比原来去食堂打饭是每个人看谁抢的赢,谁先抢到谁先吃,有了线程吃之后,就是排好队形,今天我跟你关系好,你先来吃饭。比如:一个应用要和网络打交道,有很多步骤需要访问网络,为了不阻塞主线程,每个步骤都创建个线程,在线程中和网络交互,用线程池就变的简单,线程池是对线程的一种封装,让线程用起来更加简便,只需要创一个线程池,把这些步骤像任务一样放进线程池,在程序销毁时只要调用线程池的销毁函数即可。

单个线程的弊端:

  • 每次new Thread新建对象性能差
  • 线程缺乏统一管理,可能无限制新建线程,相互之间竞争,及可能占用过多系统资源导致死机或者OOM
  • 缺乏更多功能,如定时执行、定期执行、线程中断

线程池的好处 :

  • 重用存在的线程,减少对象创建、消亡的开销,性能佳。
  • 可有效控制最大并发线程数,提高系统资源的使用率,同时避免过多资源竞争,避免堵塞。
  • 提供定时执行、定期执行、单线程、并发数控制等功能。

sleep() 和 wait() 的区别:

sleep() 来自 Thread 类,wait() 来自 Object 类
调用 sleep() 方法的过程中,线程不会释放对象锁。而调用 wait() 方法线程会释放对象锁
sleep() 睡眠后不出让系统资源,wait() 让出系统资源其他线程可以占用 CPU
sleep(milliseconds) 需要指定一个睡眠时间,时间一到会自动唤醒.

下面是用 windows live writer 写的,是在是比网页的好多了。。。。

面向对象的三大特性和五大基本原则:

三大特性:

  • 封装
  • 继承
  • 多态

五大基本原则:

  • 单一职责原则
  • 开放封闭原则
  • 替换原则
  • 依赖原则
  • 接口分离原则

详细:

封装:所谓封装,也就是把客观事物封装成抽象的类,并且类可以把自己的数据和方法只让可信的类或者对象操作,对不可信的进行信息隐藏。封装是面向对象的特征之一,是对象和类概念的主要特性。 简单的说,一个类就是一个封装了数据以及操作这些数据的代码的逻辑实体。在一个对象内部,某些代码或某些数据可以是私有的,不能被外界访问。通过这种方式,对象对内部数据提供了不同级别的保护,以防止程序中无关的部分意外的改变或错误的使用了对象的私有部分。

继承:所谓继承是指可以让某个类型的对象获得另一个类型的对象的属性的方法。它支持按级分类的概念。继承是指这样一种能力:它可以使用现有类的所有功能,并在无需重新编写原来的类的情况下对这些功能进行扩展。 通过继承创建的新类称为“子类”或“派生类”,被继承的类称为“基类”、“父类”或“超类”。继承的过程,就是从一般到特殊的过程。要实现继承,可以通过“继承”(Inheritance)和“组合”(Composition)来实现。继承概念的实现方式有二类:实现继承与接口继承。实现继承是指直接使用基类的属性和方法而无需额外编码的能力;接口继承是指仅使用属性和方法的名称、但是子类必须提供实现的能力;

多态:所谓多态就是指一个类实例的相同方法在不同情形有不同表现形式。多态机制使具有不同内部结构的对象可以共享相同的外部接口。这意味着,虽然针对不同对象的具体操作不同,但通过一个公共的类,它们(那些操作)可以通过相同的方式予以调用。

多态三大要素:

  • 继承
  • 方法重写
  • 父类引用指向子类对象

单一职责原则:指一个类的功能要单一,不能包罗万象。如同一个人一样,分配的工作不能太多,否则一天到晚虽然忙忙碌碌的,但效率却高不起来。

开放封闭原则:一个模块在扩展性方面应该是开放的而在更改性方面应该是封闭的。比如:一个网络模块,原来只服务端功能,而现在要加入客户端功能,那么应当在不用修改服务端功能代码的前提下,就能够增加客户端功能的实现代码,这要求在设计之初,就应当将服务端和客户端分开,公共部分抽象出来。

替换原则:子类应当可以替换父类并出现在父类能够出现的任何地方。比如:公司搞年度晚会,所有员工可以参加抽奖,那么不管是老员工还是新员工,也不管是总部员工还是外派员工,都应当可以参加抽奖,否则这公司就不和谐了。

依赖原则:具体依赖抽象,上层依赖下层。假设B是较A低的模块,但B需要使用到A的功能,这个时候,B不应当直接使用A中的具体类: 而应当由B定义一抽象接口,并由A来实现这个抽象接口,B只使用这个抽象接口:这样就达到了依赖倒置的目的,B也解除了对A的依赖,反过来是A依赖于B定义的抽象接口。通过上层模块难以避免依赖下层模块,假如B也直接依赖A的实现,那么就可能造成循环依赖。

接口分离原则:模块间要通过抽象接口隔离开,而不是通过具体的类强耦合起来。

设计模式:

单例设计模式:

常见的单例模式主要有懒汉和饿汉两种,懒汉分线程安全和不安全,代码上来先:

1 . 懒汉式(线程不安全),我现在喜欢把它喊懒加载

package com.msym.sort;

/**
 * 
 * @author 码上猿梦
 *  http://www.cnblogs.com/daimajun/
 *  懒汉式(线程不安全)
 */
public class Singleton {
    
    private static Singleton instance = null;
    
    //私有化构造函数,这是单例模式必须的。
    private Singleton(){}

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

2 . 懒汉式(线程安全)

package com.msym.singleton;

/**
 * 
 * @author 码上猿梦
 *  http://www.cnblogs.com/daimajun/
 *  懒汉式(线程安全)
 */

public class Singleton {
    
    private static Singleton instance = null;
    
    private Singleton(){}

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

3 . 饿汉式(静态成员变量)

package com.msym.singleton;

/**
 * 
 * @author 码上猿梦
 *  http://www.cnblogs.com/daimajun/
 *  饿汉式(静态成员变量)
 */
public class Singleton {
    
    private static Singleton instance = new Singleton();
    
    private Singleton(){}

    public static Singleton getInstance(){
        return instance;
    }
}

4 . 饿汉式(采用静态代码块)

package com.msym.singleton;

/**
 * 
 * @author 码上猿梦
 *  http://www.cnblogs.com/daimajun/
 *  饿汉式(静态代码块)
 */
public class Singleton {
    
    private static Singleton instance = null;
    
    static{
        instance = new Singleton();
    }
    
    private Singleton(){}

    public static Singleton getInstance(){
        return instance;
    }
}

5 . 采用静态内部类

package com.msym.singleton;

/**
 * 
 * @author 码上猿梦
 *  http://www.cnblogs.com/daimajun/
 *  静态内部类的形式
 */
public class Singleton {
    /**
     * 静态内部类,用于创建单例对象
     * @author 码上猿梦
     *  http://www.cnblogs.com/daimajun/
     */
    private static class SingletonHolder {
        private static final Singleton INSTANCE = new Singleton();
    }
    
    private Singleton(){}
    
    /**
     * 类名.内部类成员变量
     * @return
     */
    public static final Singleton getInstance(){
        return SingletonHolder.INSTANCE;
    }
}

6 . 双重校验锁

package com.msym.singleton;

/**
 * 
 * @author 码上猿梦
 *  http://www.cnblogs.com/daimajun/
 *  双重校验锁
 */
public class Singleton {
    /*
     * 使用volatile修饰
     */
    private volatile static Singleton instance = null;
    
    private Singleton(){ }
    
    /**
     * 两次判断对象是否为空
     * @return
     */
    private static Singleton getInstance(){
        if(instance == null){
            synchronized (Singleton.class) {
                if(instance == null){
                    instance = new Singleton();
                }
            }
        }
        return instance;
    }
}

工厂模式:

大致分三类:

  • 简单工厂模式
  • 工厂方法模式
  • 抽象工厂模式

前两者可以划分为一起,就是工厂方法模式。

那么工厂方法模式和抽象工厂 模式的对比:

工厂方法模式:
      一个抽象产品类,可以派生出多个具体产品类。  
      一个抽象工厂类,可以派生出多个具体工厂类。  
      每个具体工厂类只能创建一个具体产品类的实例。
抽象工厂模式:
      多个抽象产品类,每个抽象产品类可以派生出多个具体产品类。  
      一个抽象工厂类,可以派生出多个具体工厂类。  
      每个具体工厂类可以创建多个具体产品类的实例。  
区别:
     工厂方法模式只有一个抽象产品类,而抽象工厂模式有多个。  
     工厂方法模式的具体工厂类只能创建一个具体产品类的实例,而抽象工厂模式可以创建多个。

【下面排序默认都为升序】

排序代码:选择,插入,气泡

选择排序的思路:(n 为要排序的数字个数,都假设升序排列)

      将要排序的对象分为两个部分,一个是前端已排序(假定为升序),一个后端未排序的,每次从后端选择一个最小值,并放入前端序列的最后,这样最多循环(n - 1)次后就排序完成了。最开始时,前端部分没有元素,后端元素为全部

如图:

         image

插入排序的思路:

     像玩扑克一样,可以将牌分为两堆,每次从后一堆中抽出第一张,插入到前一堆中的恰当位置,这样最多循环(n - 1)次就完成了排序。【最开始时,前一堆只有一个元素,后一堆是剩下的元素

如图:

      image

气泡排序的思路:

     顾名思义,最大的元素会像气泡一样移至右端,利用比较相邻元素的方法,将较大的元素交换至右端,所以大的元素会不断的往右移动,直到移动到恰当的位置(就是右端元素比它大的位置)为止。【当 i 和 i + 1 没有发生过元素交换位置,就表明排序已完成

如下图:

        image

选择排序,插入排序,冒泡排序:

package com.msym.sort;
  
/**
 * 排序算法:
 *      选择,插入,气泡
 * @author 码上猿梦
 *  http://www.cnblogs.com/daimajun/
 */
public class BasicSort {
    public static void selectionSort(int[] number) {
        for (int i = 0; i < number.length - 1; i++) {
            int minIndex = i;//假设第 i 个元素最小, 最开始是假设第一个元素最小
            for (int j = i + 1; j < number.length; j++) {
                if (number[j] < number[minIndex])
                    minIndex = j;//每次循环找到后端最小值的角标
            }
            if (i != minIndex) //如果i == minIndex,说明最小值的角标就是 i,不需要交换, 这里的 i 指的是位置.
                swap(number, i, minIndex);
        }
    }
    //插入排序  
    public static void injectionSort(int[] number) {
        for (int j = 1; j < number.length; j++) {
            int tmp = number[j]; //每次抽取后一堆的第一个元素
            int i = j - 1; //前一堆元素的最后一个,用于下面遍历的起点
            while (tmp < number[i]) { //最先和前一堆最后一个元素进行比较,然后依次往前进行比较
                number[i + 1] = number[i];
                i--;
                if (i == -1)
                    break;
            }
            number[i + 1] = tmp; //将 tmp 放在 i 的位置上
        }
    }
    //冒泡排序
    public static void bubbleSort(int[] number) {
        boolean flag = true;
        for (int i = 0; i < number.length - 1 && flag; i++) {
            flag = false; //每次将标志位重置为 false
            for (int j = 0; j < number.length - i - 1; j++) {
                if (number[j + 1] < number[j]) {
                    swap(number, j + 1, j);
                    flag = true; //交换过元素就将标志位改为 true
                }
            }
        }
    }
  
    private static void swap(int[] number, int i, int j) {
        int t;
        t = number[i];
        number[i] = number[j];
        number[j] = t;
    }
}

Shaker 排序法,改良后的气泡排序:

     排序思路:

           方法就在于气泡排序的双向进行,先让气泡排序由左向右进行,再来让气泡排序由右往左进行,如此完成一次排序的动作,而您必须使用left与right两个旗标来记录左右两端已排序的元素位置。

如下图:

        image

代码如下:

package com.msym.sort;

/**
 * 改良的气泡排序
 * 
 * @author 码上猿梦 http://www.cnblogs.com/daimajun/
 */
public class ShakerSort {
	public static void sort(int[] number) {
		int i, left = 0, right = number.length - 1, shift = 0; //
		while (left < right) { 
			// 向右进行气泡排序,一次循环,最大值到达最右端
			for (i = left; i < right; i++) {
				if (number[i] > number[i + 1]) {
					swap(number, i, i + 1);
					shift = i;
				}
			}
			right = shift; 
			// 向左进行气泡排序,一次循环,最小值到达最左端
			for (i = right; i > left; i--) {
				if (number[i] < number[i - 1]) {
					swap(number, i, i - 1);
					shift = i;
				}
			}
			left = shift;
		}
	}

	private static void swap(int[] number, int i, int j) {
		int t;
		t = number[i];
		number[i] = number[j];
		number[j] = t;
	}
}

Shell 排序法 ,改良后的插入排序:

代码如下:

package com.msym.sort;

/**
 * 改良的插入排序
 * 
 * @author 码上猿梦 http://www.cnblogs.com/daimajun/
 */
public class ShellSort {
	public static void sort(int[] number) {
		int gap = number.length / 2;
		while (gap > 0) {
			for (int k = 0; k < gap; k++) {
				for (int i = k + gap; i < number.length; i += gap) {
					for (int j = i - gap; j >= k; j -= gap) {
						if (number[j] > number[j + gap]) {
							swap(number, j, j + gap);
						} else
							break;
					}
				}
			}
			gap /= 2;
		}
	}

	private static void swap(int[] number, int i, int j) {
		int t;
		t = number[i];
		number[i] = number[j];
		number[j] = t;
	}
}

快速排序 -01:

package com.msym.sort;

/**
 * 快速排序法(一)
 * 
 * @author 码上猿梦 http://www.cnblogs.com/daimajun/
 */
public class QuickSort {
	public static void sort(int[] number) {
		sort(number, 0, number.length - 1);
	}

	private static void sort(int[] number, int left, int right) {
		if (left < right) {
			int s = number[left];
			int i = left;
			int j = right + 1;
			while (true) {
				// 向右找
				while (i + 1 < number.length && number[++i] < s);
				// 向左找
				while (j - 1 > -1 && number[--j] > s);
				if (i >= j)
					break;
				swap(number, i, j);
			}
			number[left] = number[j];
			number[j] = s;
			sort(number, left, j - 1);
			// 对左边进行递回
			sort(number, j + 1, right);
			// 对右边进行递回
		}
	}

	private static void swap(int[] number, int i, int j) {
		int t;
		t = number[i];
		number[i] = number[j];
		number[j] = t;
	}

}

快速排序 -02:

package com.msym.sort;

/**
 * 快速排序法(二)
 * @author 码上猿梦
 *  http://www.cnblogs.com/daimajun/
 */
public class QuickSort {
	public static void sort(int[] number) {
		sort(number, 0, number.length - 1);
	}

	private static void sort(int[] number, int left, int right) {
		if (left < right) {
			int s = number[(left + right) / 2];
			int i = left - 1;
			int j = right + 1;
			while (true) { 
				// 向右找
				while (number[++i] < s); 
				// 向左找
				while (number[--j] > s);
				if (i >= j)
					break;
				swap(number, i, j);
			}
			sort(number, left, i - 1); // 对左边进行递回
			sort(number, j + 1, right); // 对右边进行递回
		}
	}

	private static void swap(int[] number, int i, int j) {
		int t;
		t = number[i];
		number[i] = number[j];
		number[j] = t;
	}
}

快速排序 -03:

package com.msym.sort;

/**
 * 快速排序法(三)
 * 
 * @author 码上猿梦 http://www.cnblogs.com/daimajun/
 */
public class QuickSort {
	public static void sort(int[] number) {
		sort(number, 0, number.length - 1);
	}

	private static void sort(int[] number, int left, int right) {
		if (left < right) {
			int q = partition(number, left, right);
			sort(number, left, q - 1);
			sort(number, q + 1, right);
		}
	}

	private static int partition(int number[], int left, int right) {
		int s = number[right];
		int i = left - 1;
		for (int j = left; j < right; j++) {
			if (number[j] <= s) {
				i++;
				swap(number, i, j);
			}
		}
		swap(number, i + 1, right);
		return i + 1;
	}

	private static void swap(int[] number, int i, int j) {
		int t;
		t = number[i];
		number[i] = number[j];
		number[j] = t;
	}
}

合并排序法:

package com.msym.sort;

/**
 * 合并排序法
 * 
 * @author 码上猿梦 http://www.cnblogs.com/daimajun/
 */
public class MergeSort {
	public static int[] sort(int[] number1, int[] number2) {
		int[] number3 = new int[number1.length + number2.length];
		int i = 0, j = 0, k = 0;
		while (i < number1.length && j < number2.length) {
			if (number1[i] <= number2[j])
				number3[k++] = number1[i++];
			else
				number3[k++] = number2[j++];
		}
		while (i < number1.length)
			number3[k++] = number1[i++];
		while (j < number2.length)
			number3[k++] = number2[j++];
		return number3;
	}
}

二分搜寻法:

package com.msym.search;

/**
 * 二分搜寻法(搜寻原则的代表)
 * 
 * @author 码上猿梦 http://www.cnblogs.com/daimajun/
 */
public class BinarySearch {
	public static int search(int[] number, int des) {
		int low = 0; //左
		int upper = number.length - 1; //右
		while (low <= upper) { //只要左边小于等于右边就进循环
			int mid = (low + upper) / 2; //取中间元素的角标(自动取整),然后进行比较
			if (number[mid] < des)
				low = mid + 1;
			else if (number[mid] > des)
				upper = mid - 1;
			else
				return mid;
		}
		return -1; //找不到就返回 -1
	}

	public static void main(String[] args) {
		int[] number = { 1, 4, 2, 6, 7, 3, 9, 8 };
		QuickSort.sort(number); //先排序
		int find = BinarySearch.search(number, 3); //找到值为 3 的元素
		if (find != -1)
			System.out.println("找到数值于索引" + find);
		else
			System.out.println("找不到数值");
	}
}

jdbc 基础:

package com.msym.jdbc;

import java.sql.Connection;
import java.sql.DriverManager;
import java.sql.PreparedStatement;
import java.sql.ResultSet;

/**
 * 
 * @author 码上猿梦
 *  http://www.cnblogs.com/daimajun/
 */
public class temp{
    public static void main(String[] agrs) throws Exception{
        Class.forName("com.mysql.jdbc.Driver");
        String url = "jdbc:mysql:127.0.0.1:3306/Demo_db";
        String username = "root";
        String password = "root";
        Connection conn = DriverManager.getConnection(url, username, password);
        String sql = "select * from msym_user where name = ? ";
        PreparedStatement st = conn.prepareStatement(sql);
        st.setString(1,"码上猿梦");
        ResultSet rs = st.executeQuery(sql);
        while(rs.next()){
            int id = rs.getInt("id");
            String nickName = rs.getString("nickName");
            System.out.print("id : " + id + "; " + "nick :" + nickName);
        }
    }
}

释放资源的代码:

        // 4.释放资源.
        if (rs != null) {
            try {
                rs.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
            rs = null; // 为了让JVM垃圾回收更早回收该对象.
        }

        if (st != null) {
            try {
                st.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
            st = null;
        }

        if (conn != null) {
            try {
                conn.close();
            } catch (SQLException e) {
                e.printStackTrace();
            }
            conn = null;
        }

会话技术: 【session 和 cookie】

cookie 的几点:

  • cookie 用于携带的数据量最大为 4KB
  • 一个服务器最多向一个浏览器保存20个 cookie
  • 一个浏览器最多保存300个 cookie

我不知道到的转发和重定向:

转发和重定向会清空上一个 response中缓存的数据,所以转发或重定向前的所有的 response都是无效的,只有目标资源的 response的输出才是有效的【也就是最后一个响应的数据才会被客户端接收,其他的响应数据都被清除了】

补充:

今天看到一段代码,如下:

package com.msym.jyaml;

public class Demo {

    public static void main(String[] args) {
        Integer a = 1;
        Integer b = 1;
        Integer b2 = new Integer(1);

        Integer c = 127;
        Integer d = 127;
        
        Integer e = 128;
        Integer f = 128;
        System.out.println(a==b);//true
        System.out.println(b==b2);//false
        System.out.println(c==d);//true
        System.out.println(e==f);//false
    }
}

这里涉及到了自动装箱拆箱和 Integer 的一个内部类 IntegerCache。

     结果为什么会出现这样的,那是因为 JVM 内部有一个优化机制,当数值比较小(-128~127)时,会使用 Integer 类中的一个内部类 IntegerCache类的对象将其存储起来,如果下次需要再次装箱一个相同大小的数字(-128~127之内),就直接取之前的值了,这些比较小的数值使用的是同一块存储空间,当然,如果使用了 new 操作符,那么就一定会开辟一个新的内存空间用来存储数据。

框架基础:

Spring:

IoC:从 Spring 角度来讲,之前的应用程序去 new 对象的权利被 Spring 夺取了,称为控制反转。

DI: 从应用程序的角度讲,自己不再需要 new 对象了,只需要提供依赖对象的 set 方法【使用注解后,只需要使其成为成员变量即可,不需要 set 方法了】,等待 Spring 容器注入该对象,称为依赖注入。

SpringMVC:

原文地址:https://www.cnblogs.com/daimajun/p/7090940.html