面试题学习
a left join b最多返回多少条记录,最少返回多少条记录?
a left join b将返回a中所有的记录和b中符合on条件的记录。
最少返回的记录数是a的记录数,这还是好理解的。
最多返回的记录数a*b,如下:
select * from a;
id name
1 allen
2 tim
3 jackson
select * from b;
id grade
4 a
5 b
select a.id,a.name,b.grade from a left join b on a.id != b.id;返回的记录数是6条。
PreparedStatement和Statement的区别
应尽量使用PreparedStatement,可以防止SQL注入,例如:
mysql> select * from a where name = "abc" or 1=1--"";
+------+-------- +
| id | name |
+------+-------- +
| 1 | allen |
| 2 | edison |
| 3 | tim |
+------+--------+
3 rows in set (0.00 sec)
name字段输入的是:abc" or 1=1--",本应该查不到任何记录,结果却是查到了所有的记录。
使用PreparedStatement做测试,查询到的结果是空,是不存在SQL注入的问题。PreparedStatement会先进行预编译,传递的参数会被当做字符串进行处理,这样就不会出现SQL拼接产生的问题。
String sql = "select * from a where name = ?";
String paramter = "abc\" or 1=1--\"";
Connection conn = null;
PreparedStatement pstmt = null;
ResultSet rs = null;
try {
conn = DBConnectionManager.getConnection();
pstmt = conn.prepareStatement(sql);
for (int i = 0; params != null && i < params.length; i++){
pstmt.setString(i + 1, params[i]);
}
使用PreparedStatement还需要注意的一点是,在MySQL中,字段上如果有索引并且是varchar的,查询时不加引号,会导致为普通查询,索引会失效。反之,字段上如果有索引并且是int的,查询时加不加引号都是OK的。
mysql> explain select * from rcnetnode where index_in_mib = 10100001;
+----+-------------+-----------+------+------------------+------+---------+------+------+-------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+-----------+------+------------------+------+---------+------+------+-------------+
| 1 | SIMPLE | rcnetnode | ALL | idx_index_in_mib | NULL | NULL | NULL | 203 | Using where |
+----+-------------+-----------+------+------------------+------+---------+------+------+-------------+
1 row in set (0.00 sec)
mysql> explain select * from rcnetnode where index_in_mib = "10100001";
+----+-------------+-----------+------+------------------+------------------+---------+-------+------+-------------+
| id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra |
+----+-------------+-----------+------+------------------+------------------+---------+-------+------+-------------+
| 1 | SIMPLE | rcnetnode | ref | idx_index_in_mib | idx_index_in_mib | 203 | const | 2 | Using where |
+----+-------------+-----------+------+------------------+------------------+---------+-------+------+-------------+
1 row in set (0.00 sec)
所以上面的代码中pstmt.setString(i + 1, params[i]);是没有问题的。
HashMap实现的底层原理
先从根源说起,对象的比较使用的是equals方法。比较字符串的内容用equals而不是==,是因为String类已经重写了equals,比较的是字符串的内容,而不是Object的比较内存地址。
对象的比较重写equals方法的同时会重写hashcode方法,这是因为对象如果要放到hash类的集合(hashmap,hashset)中时,hashcode是对key进行散列的依据,决定了散列的效率。
从上图我们可以发现HashMap是由数组+链表组成的,初始的容量是16,负载因子是0.75,即存储的元素超过12个时,容量自动扩充为32.元素存储的位置是key%16,比如31,31%16结果是15,(15,value)就在15的位置。
HashMap对于上述的过程做了一些优化,不仅仅是key%16了。
当调用put(key,value)时,首先获取key的hashcode,int hash = key.hashCode();
再把hash通过一下运算得到一个int h.
hash ^= (hash >>> 20) ^ (hash >>> 12);
int h = hash ^ (hash >>> 7) ^ (hash >>> 4);
再做index = h & (length-1),算出的index是(key,value)应在的位置,这样做会使index的分布尽量均匀。
这点也是好理解的,如果hashcode写的不好,简单的key%16可能会使得个别链条很长,这样效率是较低的。
HashSet的实现是用的HashMap的key。
多线程模拟多个客户进行转账操作,有一个方法是转账,要求当转出金额大于账户余额时该线程放弃执行,将执行权力交给其他的线程。
第一个反应是用yield,但是yield依赖于操作系统,不是最好的选择。
正确的应是用notifyAll通知其他线程去继续执行,需要注意的是同步块锁得对象是this,下面是个例子程序。每个线程打印5个数字,将执 行权力交给其他的线程。
使用notifyAll而不notify是因为notify更容易引起死锁而notifyAll则不会。
public class NotifyTest { public static void main(String[] args) { TestThread t = new TestThread(); Thread t1 = new Thread(t); Thread t2 = new Thread(t); Thread t3 = new Thread(t); t1.start(); t2.start(); t3.start(); } } class TestThread implements Runnable{ int sum = 100; @Override public synchronized void run() { int localNum = sum; for(int i= sum;;){ if(i<= localNum -5){ this.notifyAll(); break; }else{ System.out.println(Thread.currentThread().getName() + ":"+ i); i--; sum = i; } } } }