您的位置 首页 java

java面试基础-2

目录


异常 13

线程 13

创建线程的方法 13

Sleep 13

wait、notify、Sleep 14

Sleep、yield、join 14

线程安全 机制 14

线程同步机制 14

同步方法 14

同步代码块 14

使用特殊域变量(volatile)实现线程同步 14

使用重入锁实现线程同步 15

使用局部变量实现线程同步 15

使用阻塞队列实现线程同步 15

使用原子变量实现线程同步 16

死锁 16

abstract 17

1. 讲解 Java 集合的体系结构 17

2. Collection、Set和List的区别? 17

3. LinkedList,ArrayList,Vector,Stack,Queue区别?NodeList区别? 18

4. Comparable和Comparator区别 18

5.heap和stack有什么区别 18

6、 HashMap Hashtable 有什么区别?对比Hashtable VS HashMap 18

7、在Hashtabl上下文中同步是什么意思? 19

8、怎样使Hashmap同步? 19

9、什么时候使用Hashtable,什么时候使用HashMap 19

11. ArrayList和Vector有什么区别?HashMap和HashTable有什么区别? 19

12. WeakHashMap的区别?ConcurrentHashMap区别? 19

13.Collection和Collections 19

Iterator接口–set集合判断重复与否 19

数组和链表的区别 20

简述equals()和 hashCode () 20

一、HashMap的内部存储结构 20

三、HashTable和ConcurrentHashMap的比较 20

哈希算法 20

HashSet的实现 20

HashMap和HashSet的区别 21

HashTable和HashMap 21

常见的集合类有这些种: 21

ArrayList 21

List接口在Collection接口的基础上,有添加了自己的一系列方法: 22

WeakHashMap 22

实现Map接口的HashMap 22

创建工程 23

导包 23

代码演示 23

jdbc 涉及到的类与接口 23

DAO 模式 24

DAO模式结构 : 24

使用dao模式完成登录操作 24

jdbc批处理 24

预编译 24

Statement PreparedStatement 区别 24

什么是 sql 攻击 25

PreparedStatement使用 25

防止SQL攻击 25

PreparedStatement的使用 25

每次SQL操作都需要建立和关闭连接,这势必会消耗大量的资源开销,如何避免? 25

连接池使用 25

实现自己的连接池(具体参看CDSN和javaEE传智16) 25


异常

1.Throwable是顶层。

2.Error是致命错误,Java运行内部错误。

3.RunableException:运行时异常

NullPointException、ArrayIndexOutOfBoundsException、ArithmeticException、IllegalArgumentException

多线程

创建线程的方法

1.继承Thread类

public class ThreadName extends Thread{}

Thread t1=new ThreadName(“1”);

Thread t2=new ThreadName(“2”);

t1.start();

t2.start();

2.实现Runable接口的run()方法

package java.lang;

public interface Runable{ public abstruct void run();}

class RunableDemo implements Runable{ 重写run(){};}

new 一个实体类对象: RunableDemo r1=new RunableDemo();

new Thread对象: Thread t1=new Thread(r1);

t开启的新线程是r1的run方法: t.start();

Sleep

Thread.sleep(time):此时不会占用cpu,没有执行权,也没有执行能力,但是没有释放锁。等到time到,再进入就绪状态。

sleep在执行时不释放锁,所以同步代码块中的共享数据其他线程不能使用。

wait、notify、Sleep

1.在 synchronized 函数中,或对象调用wait(),是对希望上锁的对象加上同步。

2.wait和notify都是Object的方法。sleep(),yield是Thread的方法

3.synchronized同步的对象,sleep时间到后没有释放锁,而wait释放了锁。

4.sleep可以在任何地方使用,wait只能在同步中使用。

5.sleep必须捕获异常,wait不需要,jion必须捕获异常。

6.wait和sleep都可以通过中断interrupt进入暂停状态,但会抛出InterruptException。

Sleep、yield、join

sleep:暂停一段时间,让其他线程执行,如高优先级的sleep,低优先级的可以先执行。

yield:类似sleep,但是没有时间限制。也就是回到原来谁都没抢占资源的状态。

join:加入的线程先执行完后,原线程在执行。

线程安全机制

1.线程安全问题来源是多个线程同时存取共享数据,或同时访问某个类。

2.解决线程安全的机制

1.加锁:线程同步

2.使用原子类,除long、double外的基本类型

3.不共享状态:没有实例变量的对象;线程关闭

4.不可变对象:final对象、final修饰的引用变量(String除外)

线程同步机制

1.synchronized方法

2.synchronized代码块

3.volatile

4.reenrantLock重入锁

5.局部变量同步

6.阻塞队列同步

7.使用原子类

同步方法

即有synchronized关键字修饰的方法。

public synchronized void save(){}

注: synchronized关键字也可以修饰静态方法,此时如果调用该静态方法,将会锁住整个类

同步代码块

即有synchronized关键字修饰的语句块。

synchronized(object){ }

注:同步是一种高开销的操作,因此应该尽量减少同步的内容。通常没有必要同步整个方法,使用synchronized代码块同步关键代码即可。

使用特殊域变量(volatile)实现线程同步

1.synchronized指该代码具有 原子性(atomicity)和 可见性(visibility)。

原子性:意味着只有一个线程能够执行一段代码,从而防止多个线程在更新共享状态时相互冲突。

可见性:确保释放锁之前对共享数据做出的更改,对于随后获得该锁的另一个线程是可见的。

用volatile修饰的变量,线程在每次使用变量的时候,都会读取变量修改后最新的值

2.实现原理:

jvm运行时,有一个内存区域是jvm虚拟机栈,每一个线程运行时都有一个线程栈,线程栈保存了线程运行时候变量值信息。当线程访问某一个对象时候值的时候,首先通过对象的引用找到对应在堆内存(主内存)的变量的值,然后把堆内存变量的具体值load到线程本地内存(工作内存、寄存器)中,建立一个变量副本,之后线程就不再和对象在堆内存变量值有任何关系,而是直接修改副本变量的值,在修改完之后的某一个时刻(线程退出之前),自动把线程变量副本的值回写到对象在堆中变量。这样在堆中的对象的值就产生变化了。

3.volatile不会提供任何原子操作,它也不能用来修饰final类型的变量

对于volatile修饰的变量,jvm虚拟机只是保证从主内存加载到线程工作内存的值是最新的,

如果两个线程同时操作工作内存,那么在写进堆内存中之前还不是同步的。。

4.volatile的使用条件:要使 volatile 变量提供理想的线程安全,必须同时满足下面两个条件:

对变量的写操作不依赖于当前值:不能是count++,因为count加载–++修改写入。可以是 布尔 类型

该变量没有包含在具有其他变量的不变式中。不能是low<up

5.volatile的适用场景

1.状态标志:实现 volatile 变量的规范使用仅仅是使用一个布尔状态标志,用于指示发生了一个重要的一次性事件

2.某个线程可能会获得一个未完全初始化的实例。

使用重入锁实现线程同步

注:Reentrant Lock ()还有一个可以创建公平锁的构造方法,但由于能大幅度降低程序运行效率,不推荐使用

在JavaSE5.0中新增了一个java.util.concurrent包来支持同步。

ReentrantLock类是可重入、互斥、实现了Lock接口的锁,它与使用synchronized方法和快具有相同的基本行为和语义,并且扩展了其能力

class Bank {

private int account = 100;

//需要声明这个锁

private Lock lock = new ReentrantLock();

public int getAccount() {

return account;

}

//这里不再需要synchronized

public void save(int money) {

lock.lock();

try{

account += money;

}finally{

lock.unlock();

}

}

ReenreantLock类的常用方法有:

ReentrantLock() : 创建一个ReentrantLock实例

lock() : 获得锁

unlock() : 释放锁

注:关于Lock对象和synchronized关键字的选择:

A. 最好两个都不用,使用一种java.util.concurrent包提供的机制,能够帮助用户处理所有与锁相关的代码。

B. 如果synchronized关键字能满足用户的需求,就用synchronized,因为它能简化代码

C. 如果需要更高级的功能,就用ReentrantLock类,此时要注意及时释放锁,否则会出现死锁,通常在finally代码释放锁

使用局部变量实现线程同步

如果使用ThreadLocal管理变量,则每一个使用该变量的线程都获得该变量的副本,副本之间相互独立,这样每一个线程都可以随意修改自己的变量副本,而不会对其他线程产生影响。

ThreadLocal 类的常用方法:

1.ThreadLocal() : 创建一个线程本地变量

2.get() : 返回此线程局部变量的当前线程副本中的值

3.initialValue() : 返回此线程局部变量的当前线程的”初始值”

4.set(T value) : 将此线程局部变量的当前线程副本中的值设置为value

//使用ThreadLocal类管理共享变量account

private static ThreadLocal<Integer> account = new ThreadLocal<Integer>()

使用阻塞队列实现线程同步

前面同步方式都是在底层实现的线程同步,但是我们在实际开发当中,应当尽量远离底层结构。使用javaSE5.0版本中新增的java.util.concurrent包将有助于简化开发。

主要是使用LinkedBlockingQueue<E>来实现线程的同步。LinkedBlockingQueue<E>是一个基于已连接节点的,范围任意的blocking queue。队列是先进先出的顺序(FIFO)。

LinkedBlockingQueue 类常用方法:

LinkedBlockingQueue() : 创建一个容量为Integer.MAX_VALUE的LinkedBlockingQueue

put(E e) : 在队尾添加一个元素,如果队列满则阻塞

size() : 返回队列中的元素个数

take() : 移除并返回队头元素,如果队列空则阻塞

BlockingQueue<E>定义了阻塞队列的常用方法,尤其是三种添加元素的方法,我们要多加注意,当队列满时:

add()方法会抛出异常  offer()方法返回false  put()方法会阻塞

//定义一个阻塞队列用来存储生产出来的商品

private LinkedBlockingQueue<Integer> queue = new LinkedBlockingQueue<Integer>();

int n = queue.take();

使用原子变量实现线程同步

需要使用线程同步的根本原因在于对普通变量的操作不是原子的。在java的util.concurrent.atomic包中提供了创建了原子类型变量的工具类,

其中AtomicInteger 表可以用原子方式更新int的值,可用在应用程序中(如以原子方式增加的计数器),但不能用于替换Integer;

AtomicInteger类常用方法:

AtomicInteger(int initialValue) : 创建具有给定初始值的新的AtomicInteger

addAddGet(int dalta) : 以原子方式将给定值与当前值相加

get() : 获取当前值

补充–原子操作主要有:

对于引用变量和大多数原始变量(long和double除外)的读写操作;

对于所有使用volatile修饰的变量(包括long和double)的读写操作。

死锁

产生死锁的原因:线程在获得一个锁1的情况下,再去调用另一个锁2,也就是锁1包含了锁2.

class B{

private String address;

public synchronized void method1(){

//do smothing

}

}

public class A{

B b=new B();

public synchronized void method2(){

b.method1();

}

}

应改成:

public class A{

B b=new B();

public void method2(){

synchronized(this){

//do something

}

b.method1();

}

}


集合框架

以下是笔者总结的一些面试中经常会问的相关问题:

1. 讲解java集合的体系结构

List、Set、Map是这个集合体系中最主要的三个接口。

List和Set继承自Collection接口。

List有序且允许元素重复。ArrayList、LinkedList和Vector是三个主要的实现类。

Set不允许元素重复。HashSet和TreeSet是两个主要的实现类。

Map也属于集合系统,但和Collection接口不同。Map是key对value的映射集合,其中key列就是一个集合。key不能重复,但是value可以重复。 HashMap、TreeMap和Hashtable是三个主要的实现类。SortedSet和SortedMap接口对元素按指定规则排序,SortedMap是对key列进行排序

2. Collection、Set和List的区别?

Collection对象之间没有指定的顺序,允许有重复元素和多个null元素对象;它是Set和List接口的父类,是一种最通用型的集合接口;

Set各个元素对象之间没有指定的顺序,不允许有重复元素,最多允许有一个null元素对象;

List各个元素对象之间有指定的顺序,允许重复元素和多个null元素对象;

3. LinkedList,ArrayList,Vector,Stack,Queue区别?NodeList区别?

1)LinkedList链式访问,以指针相连,适合于在链表中间需要频繁进行插入和删除操作。

2)ArrayList类似数组的形式,按照序号存储,随机访问速度非常快。

3)Vector向量按照各元素序号存储,数组大小可以动态增长,对于大容量数据存储效率较高。

4)Stack堆栈,先进后出的数组。

这些类的对比与选择:

如果涉及到堆栈队列等操作,应该考虑List中的stack,queue

对于需要快速插入删除元素,应该使用linkedlist

如果需要快速随机访问元素,应该选用arraylist

如果程序在单线程环境中,选用非同步类

如果在多线程中,选用同步类vector、stack和hashtable以及其子类。

4. Comparable和Comparator区别

1.Comparable是排序接口。即实现Comparable接口的类支持排序。

实现Comparable接口的类的对象的List列表(或数组),通过 Collections.sort(或 Arrays.sort)进行排序。

Comparable 接口仅仅只包括一个函数,它的定义如下:

package java.lang;import java.util.*;

public interface Comparable<T> {

public int compareTo(T o);

}

2.Comparator 是比较器接口。

若需要对某个类排序,而该类本身不支持排序(即没有实现Comparable接口),可以建立一个“该类的比较器”来进行排序。

这个“比较器”可以通过“实现Comparator类来新建一个比较器”,然后通过该比较器对类进行排序。

Comparator 接口仅仅只包括两个个函数,它的定义如下:

package java.util;

public interface Comparator<T> {

int compare(T o1, T o2);

boolean equals(Object obj);

}

Comparable是排序接口;若一个类实现了Comparable接口,就意味着“该类支持排序”。

而Comparator是比较器;我们若需要控制某个类的次序,可以建立一个“该类的比较器”来进行排序。

我们不难发现:Comparable相当于“内部比较器”,而Comparator相当于“外部比较器”。

5.heap和stack有什么区别

1.heap是堆,stack是栈。

2.stack的空间由操作系统自动分配和释放,heap的空间是手动申请和释放的,heap常用new关键字来分配。

3.stack空间有限,heap的空间是很大的自由区。

若只是声明一个对象,则先在栈内存中为其分配地址空间,

若再new一下,实例化它,则在堆内存中为其分配地址。

6、HashMap与HashTable有什么区别?对比Hashtable VS HashMap

两者都是用key-value方式获取数据。

Hashtable是原始集合类之一(也称作遗留类)。HashMap作为新集合框架的一部分在Java2的1.2版本中加入。

它们之间有一下区别:

Hashtable的方法是同步的,HashMap未经同步,所以在多线程场合要手动同步。

除构造函数外,Hashtable的所有 public 方法声明中都有 synchronized 关键字,HashMap没有。

Hashtable不允许 null 值(key 和 value 都不可以),HashMap允许 null 值(key和value都可以)。

两者的遍历方式大同小异,Hashtable仅仅比HashMap多一个elements方法。

HashTable使用Enumeration,HashMap使用Iterator

哈希值的使用不同,Hashtable直接使用对象的hashCode,而HashMap重新计算hash值,而且用与代替求模

Hashtable中hash数组默认大小是11,增加的方式是 old*2+1。HashMap中hash数组的默认大小是16,而且一定是2的指数

HashMap继承了AbstractMap,而HashTable继承了Dictionary

7、在Hashtabl上下文中同步是什么意思?

同步意味着在一个时间点只能有一个线程可以修改哈希表,任何线程在执行hashtable的更新操作前需要获取对象锁,其他线程等待锁的释放。

8、怎样使Hashmap同步?

HashMap可以通过Map m = Collections.synchronizedMap(hashMap)来达到同步的效果。

9、什么时候使用Hashtable,什么时候使用HashMap

基本的不同点是Hashtable同步HashMap不是的,所以无论什么时候有多个线程访问相同实例的可能时,就应该使用Hashtable,反之使用HashMap。

如果在将来有一种可能—你需要按顺序获得键值对的方案时,HashMap是一个很好的选择,因为有HashMap的一个子类 LinkedHashMap。所以如果你想 可 预测的按顺序迭代(默认按插入的顺序),你可以很方便用LinkedHashMap替换HashMap。

反观要是使用的Hashtable就没那么简单了。同时如果有多个线程访问HashMap,Collections.synchronizedMap()可以代替,总的来说HashMap更灵活。

10、为什么Vector类认为是废弃的或者是非官方地不推荐使用?或者说为什么我们应该一直使用ArrayList而不是Vector

你应该使用ArrayList而不是Vector是因为默认情况下你是非同步访问的,Vector同步了每个方法,你几乎从不要那样做,通常有想要同步的是整个操作序列。同步单个的操作也不安全(如果你迭代一个Vector,你还是要加锁,以避免其它线程在同一时刻改变集合).而且效率更慢。当然同样有锁的开销即使你不需要,这是个很糟糕的方法在默认情况下同步访问。你可以一直使用Collections.sychronizedList来装饰一个集合。

事实上Vector结合了“可变数组”的集合和同步每个操作的实现。这是另外一个设计上的缺陷。Vector还有些遗留的方法在枚举和元素获取的方法,这些方法不同于List接口,如果这些方法在代码中程序员更趋向于想用它。尽管枚举速度更快,但是他们不能检查如果集合在迭代的时候修改了,这样将导致问题。尽管以上诸多原因,Oracle也从没宣称过要废弃Vector。

11. ArrayList和Vector有什么区别?HashMap和HashTable有什么区别?

Vector和HashTable是线程同步的(synchronized)。

性能上,ArrayList比Vector要好。

12. WeakHashMap的区别?ConcurrentHashMap区别?

WeakHashMap是一种改进的hashmap,他对key进行弱引用,如果一个key不再被外部引用,则被gc回收;

ConcurrentHashMap是Java 5中支持高并发、高吞吐量的线程安全HashMap实现。允许多个修改操作并发进行,其关键在于使用了锁分离技术。

13.Collection和Collections

Collection是一系列单值集合类的父接口,提供了基本的一些方法,而Collections则是一系列算法的集合。

里面的属性和方法基本都是static的,也就是说我们不需要实例化,直接可以使用类名来调用。下面是Collections类的一些功能列表:

Collections中的单元素集合指的是集合中只有一个元素而且集合只读。

Collections.singletonList——用来生成只读的单一元素的List

Collections.singletonMap——用来生成只读的单Key和Value组成的Map

Collections.singleton——用来生成只读的单一元素的Set

Collections类提供一系列同步方法,为一些非线程安全的集合类提供同步机制。

Iterator接口–set集合判断重复与否

1. HashSet 能够快速定位一个元素,要注意的是:存入HashSet中的对象必须实现HashCode()方法;

TreeSet 将放入其中的元素按序存放。

2. Iterator接口

Iterator接口位于java.util包中,它是一个对集合进行迭代的迭代器。

用Iterator去选取容器中的元素,它将容器转换成一个序列。

Iterator iter=Object.iterator();

while(iter.hasNext()){ }

3. Iterator与ListIterator有什么区别?

Iterator:只能正向遍历集合,适用于获取移除元素。

ListIerator:继承Iterator,可以双向列表的遍历,同样支持元素的修改。

数组和链表的区别

数组是将元素在内存中连续存放,适合根据下标查询,但是不适合删改,因为需要移动大量元素。

链表恰好相反,链表中的元素在内存中不是顺序存储的,在链表中一部分存数据,一部分存指针,指向下一个元素。如果要访问链表中一个元素,需要从第一个元素开始,一直找到需要的元素位置。但是增加和删除就非常简单了,只要修改元素中的指针就可以了。如果应用需要经常插入和删除元素你就需要用链表数据结构了。

简述equals()和hashCode()

1.equals方法和hashCode方法是Object中的

2. equals()相等的两个对象,hashcode()一定相等,equals()不相等的两个对象,却并不能证明他们的hashcode()不相等。

hashcode()不等,一定能推出equals()也不等;hashcode()相等,equals()可能相等,也可能不等。

3.这就好比字典:(hashcode)学=学习+学校(equals),用equals查学习和学校不同,但得到相同的hashcode学

一、HashMap的内部存储结构

Java中数据存储方式最底层的两种结构,一种是数组,另一种就是链表。

1、数组的特点:连续空间,寻址迅速,但是在删除或者添加元素的时候需要有较大幅度的移动,所以查询速度快,增删较慢。

2、链表正好相反,由于空间不连续,寻址困难,增删元素只需修改指针,所以查询慢、增删快。

3、有没有一种数据结构来综合一下数组和链表,以便发挥他们各自的优势?答案是肯定的!就是:哈希表。

哈希表具有较快(常量级)的查询速度,及相对较快的增删速度,所以很适合在海量数据的环境中使用。

哈希表是由数组+链表组成的,一个长度为16的数组中,每个元素存储的是一个链表的头结点。

这些元素存储到数组的规则:hash(key)%len,元素的key的哈希值对数组长度取模得到。

如12%16=12,28%16=12,所以12、28都存储在数组下标为12的位置。

三、HashTable和ConcurrentHashMap的比较

ConcurrentHashMap是线程安全的HashMap的实现。同样是线程安全的类,它与HashTable在同步方面有什么不同呢?

synchronized关键字加锁的原理,其实是对对象加锁,不论你是在方法前加synchronized还是语句块前加,锁住的都是对象整体。

但是ConcurrentHashMap的同步机制和这个不同,它不是加synchronized关键字,而是基于lock操作的。

这样的目的是保证同步的时候,锁住的不是整个对象。

事实上,ConcurrentHashMap可以满足concurrentLevel个线程并发无阻塞的操作集合对象。

哈希算法

1.查找一个集合中是否包含有某个对象,使用equals方法比较,但是假如有一万个元素就要比较一万个对象内容。

2.有人发明了一种哈希算法来提高从集合中查找元素的效率。每个对象对应一个hash码,再将hash码分组。这样可以根据hash码找到相应的分组,最后取得该存储区域内的每个元素与该对象进行equals方法比较。

HashSet的实现

HashSet就是采用哈希算法存取对象的集合,它内部采用对某个数字n进行取余的方式对哈希码进行分组和划分对象的存储区域;Object类中定义了一个hashCode()方法来返回每个Java对象的哈希码,当从HashSet集合中查找某个对象时,Java系统首先调用对象的hashCode()方法获得该对象的哈希码表,然后根据哈希吗找到相应的存储区域,最后取得该存储区域内的每个元素与该对象进行equals方法比较;这样就不用遍历集合中的所有元素就可以得到结论

HashMap和HashSet的区别

什么是HashSet

HashSet实现了Set接口,它不允许集合中有重复的值,当我们提到HashSet时,第一件事情就是在将对象存储在HashSet之前,要先确保对象重写equals()和hashCode()方法,这样才能比较对象的值是否相等,以确保set中没有储存相等的对象。如果我们没有重写这两个方法,将会使用这个方法的默认实现。

什么是HashMap

HashMap实现了Map接口,Map接口对键值对进行映射。Map中不允许重复的键。Map接口有两个基本的实现,HashMap和TreeMap。TreeMap保存了对象的排列次序,而HashMap则不能。HashMap允许键和值为null。HashMap是非synchronized的。

HashTable和HashMap

采用相同的存储机制,二者的实现基本一致,不同的是:

1、HashMap是非线程安全的,HashTable是线程安全的,内部的方法基本都是synchronized。

2、HashTable不允许有null值的存在。在HashTable中调用put方法时,如果key为null,直接抛出NullPointerException。

常见的集合类有这些种:

Arrays:java.util.Arrays

public class Arrays extends Object

Array是Java特有的数组。在你知道所要处理数据元素个数的情况下非常好用。java.util.Arrays 包含了许多处理数据的实用方法:

Arrays.asList (T… a): 可以从 Array 转换成 List。可以作为其他集合类型构造器的参数。

Arrays.binarySearch(byte[] a, byte key) : 在一个已排序的或者其中一段中快速查找。

Arrays.copyOfcopyOf(T[] original, int newLength) : 如果你想扩大数组容量又不想改变它的内容的时候可以使用这个方法。

Arrays.copyOfRange(T[] original, int from, int to) : 将指定数组的指定范围复制到一个新数组。。

Arrays.deepEquals、Arrays.deepHashCode: Arrays.equals/hashCode的高级版本,支持子数组的操作。

Arrays.equals(T[] a1, T[] a2) :

如果你想要比较两个数组是否相等,应该调用这个方法而不是数组对象中的 equals方法

数组对象中没有重写equals()方法,所以这个方法之比较引用而不比较内容。

这个方法集合了Java 5的自动装箱和无参变量的特性,来实现将一个变量快速地传给 equals() 方法

所以这个方法在比较了对象的类型 之后是直接传值 进去比较的。

Arrays.fill(T] a, T val) : 用一个给定的值填充整个数组或其中的一部分。

Arrays.hashCode(T[] a) : 用来根据数组的内容计算其哈希值(数组对象的hashCode()不可用)。这个方法集合了Java 5的自动装箱和无参变量的特

性,来实现将一个变量快速地传给 Arrays.hashcode方法——只是传值进去,不是对象。

Arrays.sorT(int[] a,[ int fromIndex, int toIndex]) : 对整个数组或者数组的一部分进行排序。也可以使用此方法用给定的比较器对对象数组进行排序。

Arrays.toString(T[] a) : 打印数组的内容。

System.arraycopy: 如果想要复制整个数组或其中一部分到另一个数组,可以调用 方法。

此方法从源数组中指定的位置复制指定个数的元素到目标数组里。

ArrayList

ArrayList底层采用数组实现,具有较高的查询速度。这些方法也就是Set和List及它们的实现类里有的方法。

boolean isEmpty(): 如果容器里面没有保存任何元素,就返回true。

boolean contains(Object): 如果容器持有参数Object,就返回true。

Iterator iterator(): 返回一个可以在容器的各元素之间移动的Iterator。

Object[] toArray(): 返回一个包含容器中所有元素的数组。

Object[] toArray(Object[] a):返回一个包含容器中所有元素的数组,且数组不是普通Object数组,它的类型应该同参数数组a的类型相同(要做类型转换)。

void clear(): 清除容器所保存的所有元素。(“可选”)

boolean remove(Object o);

boolean add(Object): 确保容器能持有你传给它的那个参数。如果没有把它加进去,就返回false。(这是个“可选”的方法,本章稍后会再作解释。)

boolean addAll(Collection): 加入参数Collection所含的所有元素。只要加了元素,就返回true。

boolean containsAll(Collection):如果容器持有参数Collection所含的全部元素,就返回true

boolean removeAll(Collection): 删除容器里面所有参数Collection所包含的元素。只要删过东西,就返回true。(“可选”)

boolean retainAll(Collection): 只保存参数Collection所包括的元素(集合论中“交集”的概念)。如果发生过变化,则返回true。(“可选”)

boolean equals(Object o);

int hashCode();

int size():返回容器所含元素的数量。

List接口在Collection接口的基础上,有添加了自己的一系列方法:

E get(int index);

E set(int index, E element);

void add(int index, E element);

E remove(int index);

int indexOf(Object o);

int lastIndexOf(Object o);

ListIterator<E> listIterator();

ListIterator<E> listIterator(int index);

List<E> subList(int fromIndex, int toIndex);

List<String> list = new ArrayList<String>( );

List<String> list = new ArrayList<String>(5);

LinkedList

ArrayList基于数组实现,所以它具备数组的特点,即查询速度较快,但是修改、插入的速度却有点儿慢,但是,下面将要介绍的LinkedList就是来解决这个问题的,LinkedList基于链表,与ArrayList互补,所以实际开发中我们应该按照自己的需求来定到底用哪一个。

LinkedList底层采用双向循环列表实现,进行插入和删除操作时具有较高的速度,我们还可以使用LinkedList来实现队列和栈。

WeakHashMap

理解该集合类之前,建议先去了解Java的垃圾回收机制,WeakHashMap多用于缓存系统,就是说在系统内存紧张的时候可随时进行GC,但是如果内存不紧张则可以用来存放一些缓存数据。因为如果使用HashMap的话,它里面的值基本都是强引用,即使内存不足,它也不会进行GC,这样系统就会报异常。

实现Map接口的HashMap

Set的实现类HashSet,底层还是调用Map接口来处理,所以,Map接口及其实现类的一些方法。Map接口中的原始方法有:

public abstract int size();

public abstract boolean isEmpty();

public abstract boolean containsKey(Object paramObject);

public abstract boolean containsValue(Object paramObject);

public abstract V get(Object paramObject);

public abstract V put(K paramK, V paramV);

public abstract V remove(Object paramObject);

public abstract void putAll(Map<? extends K, ? extends V> paramMap);

public abstract void clear();

public abstract Set<K> keySet();

public abstract Collection<V> values();

public abstract Set<Entry<K, V>> entrySet();

public abstract boolean equals(Object paramObject);

public abstract int hashCode();

此处细心的读者会看到,每个方法前都有abstract关键字来修饰,其实就是说,接口中的每个方法都是抽象的,有的时候我们写的时候不加abstract关键字,但是在编译的过程中,JVM会给加上的,所以这点要注意。抽象的方法意味着它没有方法实现体,同时必须在实现类中重写,接下来我们依次分析一下他们的实现类HashMap中是怎么做的。

JDBC

创建工程

选择”Properties”,在”Java Bulid Path”中选择”Add External JARs…”,选择下载并解压后获得的jar包。

mysql-connector-java-5.1.10-bin.jar,放在Web app libraries和WEB-INF/lib

导包

import com.mysql.jdbc.* (Connection;PreparedStatement;)

import java.sql.* (DriverManager;ResultSet;SQLException;)

代码演示

(1)定义记录的类

static class Student {

private String Id;

private String Name;

Student(String Name) {

this.Id = null; //default

this.Name = Name;

}

public String getId() {

return Id;

}

public String getName() {

return Name;

}

(2)连接的获取

private static Connection getConn() {

String driver = “com.mysql.jdbc.Driver”;

String url = “jdbc:mysql://localhost:3306/samp_db”;

String username = “root”;

String password = “”;

Connection conn = null;

try {

Class.forName(driver); //classLoader,加载对应驱动

conn = (Connection) DriverManager.getConnection(

url, username, password);

} catch (ClassNotFoundException e) {

e.printStackTrace();

} catch (SQLException e) {

e.printStackTrace();

}

return conn;

}

(3)insert

private static int insert(Student student) {

Connection conn = getConn();

int i = 0;

String sql = “insert into students (Name,Sex,Age) values(?,?,?)”;

PreparedStatement pstmt;

try {

pstmt = (PreparedStatement) conn.prepareStatement(sql);

pstmt.setString(1, student.getName());

//pstmt.setString(2, student.getSex());

//pstmt.setString(3, student.getAge());

i = pstmt.executeUpdate();

pstmt.close();

conn.close();

} catch (SQLException e) {

e.printStackTrace();

}

return i;

}

(4)update

String sql = “‘ where Name='” + student.getName() ;

其他与insert一样。

(5)select

String sql = “select * from students”;

try {

pstmt = (PreparedStatement) conn.prepareStatement(sql);

ResultSet rs = pstmt.executeQuery();

//getMetaData()返回ResultSetMetaData

//获取此ResultSet对象的列的编号、类型和属性。

//接口 ResultSetMetaData返回此 ResultSet 对象中的列数。

int col = rs.getMetaData().getColumnCount();

while (rs.next()) {

for (int i = 1; i <= col; i++) {

System.out.print(rs.getString(i) + “t”);

if ((i == 2) && (rs.getString(i).length() < 8)) {

System.out.print(“t”);

}

}

System.out.println(“”);

}

} catch (SQLException e) {

e.printStackTrace();

}

return null;

}

(6)delete

String sql = “delete from students where Name='” + name + “‘”;

jdbc涉及到的类与接口

1. Java.sql 类:DriverManger 接口 它是用于调用存储过程

2. Javax .sql 接口 :DataSource接口 public interface Connection extends Wrapper

提供描述其表、所支持的 SQL 语法、存储过程、此连接功能等等的信息。

此信息是使用 getMetaData 方法获得的。

3.PreparedStatement接口 public interface PreparedStatement extends Statement表示预编译的 SQL 语句的对象。

4.ResultSet 接口 public interface Result etextends Wrapper表示数据库结果集的数据表,通过执行查询数据库的语句生成。

5. static {

try {// 将加载驱动操作,放置在静态代码块中.这样就保证了只加载一次.

Class.forName(DRIVERCLASS);

} catch (ClassNotFoundException e) {

e.printStackTrace();

}

}

DAO模式

DAO模式(Data Access Object 数据访问对象):在持久层通过DAO将数据源操作完全封装起来,业务层通过操作Java对象,完成对数据源操作

DAO模式结构 :

1、数据源(MySQL数据库)

2、Business Object 业务层代码,调用DAO完成 对数据源操作

3、DataAccessObject 数据访问对象,持久层DAO程序,封装对数据源增删改查,提供方法参数都是Java对象

4、TransferObject 传输对象(值对象) 业务层通过向数据层传递 TO对象,完成对数据源的增删改查

使用dao模式完成登录操作

1.web层 login.jsp LoginServlet User

2.service层 UserService

3.dao层 UserDao

jdbc批处理

一次可以执行多条sql语句. 在jdbc中可以执行sql语句的对象有Statement,PreparedStatement,它们都提供批处理.

1. addBatch(String sql); 将sql语句添加到批处理

executeBatch(); 执行批处理

clearBatch();

预编译

数据库提供分离数据和代码(指令)的方式,就是SQL预编译。SQL语句进行预编译,就是在程序运行时第一次操作数据库之前,SQL语句已经被数据库分析,编译和优化,对应的执行计划也会缓存下来并允许数据库已参数化的形式进行查询。

Statement PreparedStatement区别

1.PreparedStatement是预编译的,对于批量处理可以大大提高效率。

2.使用 Statement 对象。在对数据库只执行一次性存取的时侯,用Statement对象进行处理。

PreparedStatement对象的开销比Statement大,对于一次性操作并不会带来额外的好处。

Code Fragment 1:

String updateString = “UPDATE COFFEES SET SALES = 75 ” + “WHERE COF_NAME LIKE ′Colombian′”;

stmt.executeUpdate(updateString);

Code Fragment 2:

PreparedStatement updateSales = con.prepareStatement(“UPDATE COFFEES SET SALES = ? WHERE COF_NAME LIKE ? “);

updateSales.setInt(1, 75);

updateSales.setString(2, “Colombian”);

updateSales.executeUpdate();

后者使用了PreparedStatement对象只需运行SQL语句,而不必先编译。

1.Statement它更适合执行不同sql的批处理。它没有提供预处理功能,性能比较低。

2.PreparedStatement它适合执行相同sql的批处理,它提供了预处理功能,性能比较高。

选择PreparedStatement对象与否,在于相同句法的SQL语句是否执行了多次,而且两次之间的差别仅仅是变量的不同。

PreparedStatement相当于为预编译提供一个模板。

所以向PreparedStatement中添加的不是SQL语句,而是给“?”赋值。

注意;mysql默认情况下,批处理中的预处理功能没有开启,需要开启

1.在 url下添加参数:url=jdbc:mysql:///day17?useServerPrepStmts=true&cachePrepStmts=true&rewriteBatchedStatements=true

2.注意驱动版本:Mysql驱动要使用mysql-connector-java-5.1.13以上

什么是SQL攻击

在需要用户输入的地方,用户输入的是SQL语句的片段,最终用户输入的SQL片段与我们DAO中写的SQL语句合成一个完整的SQL语句!

public void login(String username, String password) login(“a ‘ or ‘ a ‘=’ a”, “a ‘ or ‘ a ‘=’ a”);

因为是输入的用户名和密码是SQL语句片段,最终与我们的login()方法中的SQL语句组合在一起!我们来看看组合在一起的SQL语句:

SELECT * FROM tab_user WHERE username=’a’ or ‘a’=’a’ and password=’a’ or ‘a’=’a’

sql注入由于没有对用户输入进行充分检查,而SQL又是拼接而成,在用户输入参数时,在参数中添加一些SQL 关键字,达到改变SQL运行结果的目的,也可以完成恶意攻击。

示例: 在输入用户名时 tom ‘ or ‘ 1 ‘=’ 1这时就不会验证密码了。

解决方案: PreparedStatement(重点),它是一个预处理的Statement,它是java.sql.Statement接口的一个子接口。

PreparedStatement使用

防止SQL攻击

l 过滤用户输入的数据中是否包含非法字符;

l 分步交验!先使用用户名来查询用户,如果查找到了,再比较密码;

l 使用PreparedStatement。

PreparedStatement的使用

1.在sql语句中,使用”?”占位 String sql=”select * from user where username=? and password=?”;

2.得到PreparedStatement对象 PreparedStatement pst=con.prepareStatement(String sql);

3.对占位符赋值 pst.setXxx(int index,Xxx obj); 例如: setInt() setString();参数index,代表的是”?”的序号.注意:从1开始。

4.执行sql DML: pst.executeUpdate(); DQL: pst.executeQuery(); 注意:这两方法无参数

关于PreparedStatement优点: 1.解决sql注入(具有预处理功能) 2.不需要在拼sql语句。

String sql = “select * from tab_student where s_number=?”;

PreparedStatement pstmt = con.prepareStatement(sql);

pstmt.setString(1, “S_1001”);

ResultSet rs = pstmt.executeQuery();

rs.close();

pstmt.clearParameters();

pstmt.setString(1, “S_1002”);

rs = pstmt.executeQuery();

建议大家在今后的开发中,无论什么情况,都去需要PreparedStatement,而不是使用Statement

每次SQL操作都需要建立和关闭连接,这势必会消耗大量的资源开销,如何避免?

可以采用连接池,对连接进行统一维护,不必每次都建立和关闭。事实上这是很多对JDBC进行封装的工具所采用的。

连接池使用

1.在用户和数据库之间创建一个”池”,它有若干个连接对象,当用户想要连接数据库,就要先从连接池中获取连接对象,然后操作数据库。一旦连接池中的连接对象被拿光了,下一个想要操作数据库的用户必须等待,等待其他用户释放连接对象,把它放回连接池中这时候等待的用户才能获取连接对象,从而操作数据库。

2.属性:连接对象初始的数量:initSize,连接对象最大数量:maxSize

实现自己的连接池(具体参看CDSN和javaEE传智16)

1.实现了javax.sql.DataSource的才是标准的数据库连接池:class MyDataSource implements DataSource

2.//设置连接池属性 private int initSize = 5; private int maxSize = 8;

3.//用LinkedList对象来保存connection对象

private LinkedList<Connection> connList = new LinkedList<Connection>();

6. DBCP连接池是开源组织Apache软件基金组织开发的连接池实现。事实上,tomcat服务器默认就会使用这个连接池道具。

JDBC代码实例

1.加载JDBC驱动:

try{

Class.forName(“com.mysql.jdbc.Driver”/”oracle.jdbc.oracleDriver”);

}catch(ClassNotFoundException e){

System.out.println(“”);

}

2.提供JDBC连接的URL

jdbc:mysql://localhost:3306/test?userUnicode=true&characterEncoding=gbk;

协议:子协议://主机:端口号/文件数据库?属性

3.创建数据库连接:向java.sql.DriverManager请求获得Connection对象

String url=” jdbc:mysql://localhost:3306/test”

String username=”root”;

String password=”123”;

try{

Connection conn = DriverManager.getConnection(url,username,password);

}catch(SQLException se){

System.out.println(“连接失败”);

se.printStackTrace();

}

4.创建一个statement对象执行SQL

Statement stmt=conn.createStatement(); 静态SQL

PreparedStatement pstmt=conn.prepareStatement(“sql语句”); 动态SQL

CallableStatement cstmt=conn.prepareCall(“{CALL demo(??)}”) 调用存储过程

5.执行SQL语句 executeQuery、executeUpdate、execute

ResultSet rs=stmt.executeQuery(String sqlString);

int rows=stmt.executeUpdate(String sql);

boolean flag=stmt.execute(sql);

6.读取ResultSet

while(rs.next(){

String name=rs.getString(“name”);

String pass=rs.getString(1);//从第一列开始

}

7.关闭JDBC

if(rs!=null){

try{

rs.close();

}catch(SQLException e){

e.printStackTrace();

}

}

文章来源:智云一二三科技

文章标题:java面试基础-2

文章地址:https://www.zhihuclub.com/193008.shtml

关于作者: 智云科技

热门文章

网站地图