Java面试(三)
1 哈希函数满足什么条件?
计算简单/散列地址分布均匀。
3 System.gc() VS Runtime.gc()
java.lang.System.gc()只是java.lang.Runtime.getRuntime().gc()的简写
提示JVM进行垃圾回收。但是不保证立即执行。
4 finalize() 什么时候调用?
在释放对象占用的内存之前,垃圾收集器会调用对象的finalize()方法,一般建议在该方法中释放对象持有的资源。
5 对象的引用设置为null,垃圾收集器是否立即释放对象占用的内存。
不会。在下个垃圾回收周期中,这个对象将是可被回收的。
6 JDBC
JDBC允许用户在不同的数据库之间做选择的一个抽象层。JDBC允许开发者用JAVA写数据库应用程序,而不关心底层数据库细节。
package interview; import java.sql.CallableStatement; import java.sql.Connection; import java.sql.DriverManager; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; import java.sql.Statement; public class JDBCTest { public static void main(String[] args) { try { Class.forName("com.mysql.jdbc.driver"); } catch (ClassNotFoundException e) { System.out.println("找不到驱动程序类 ,加载驱动失败!"); } // JDBC连接的URL:协议:子协议:数据源标识 String url = "jdbc:mysql://localhost:3306/test?useUicode=true"; // 创建数据库连接 String username = "root"; String password = "root"; Connection con = null; try { con = DriverManager.getConnection(url, username, password); } catch (SQLException e) { System.out.println("数据库连接失败!"); e.printStackTrace(); } /* * 创建statement: 要执行SQL语句,必须获得java.sql.Statement实例,Statement实例分为以下3 种类型: * 1、 执行静态SQL语句。通常通过Statement实例实现。 2、 * 执行动态SQL语句通常通过PreparedStatement实例实现。 3、 * 执行数据库存储过程。通常通过CallableStatement实例实现。 * 注意:在SQL中如果某些参数没有确定,如"select * from t1 where c1>? and c2<?",这种语句是静态SQL * ,不是动态SQL,虽然个别参数的值不知道,但整个SQL的结构已经确定,数据库是可以将它编译的,在执行阶段只需将个别参数的值补充进来即可 * 具体的实方式: */ Statement stmt = null; try { stmt = con.createStatement(); PreparedStatement pstmt = con.prepareStatement(sql); CallableStatement cstmt = con.prepareCall("{CALL demoSp(? , ?)}"); } catch (SQLException e) { // TODO Auto-generated catch block e.printStackTrace(); } /* * 执行SQL: Statement接口三种SQL方法:executeQuery, executeUpdate, execute * ResultSet executeQuery(String sql) : 查询,返回结果集。 Int * executeUpdate(String sql) : insert/update/delete, DDL(create table, * drop table) Execute(sql) : 返回多个结果集,多个更新计数或二者结合的语句。 */ ResultSet rs = stmt.executeQuery("xx"); int rows = stmt.executeUpdate("xx"); Boolean flag = stmt.execute("xxx"); // 处理结果: while (rs.next()) { String name = rs.getString("name"); // String pass = rs.getString(1); // 这个方法高效,列是从左到右,从1开始 } // 关闭JDBC对象 // 关闭记录集 ->关闭声明->关闭连接对象。 if (rs != null) { // 关闭记录集 try { rs.close(); } catch (SQLException e) { e.printStackTrace(); } } if (stmt != null) { // 关闭声明 try { stmt.close(); } catch (SQLException e) { e.printStackTrace(); } } if (con != null) { // 关闭连接对象 try { con.close(); } catch (SQLException e) { e.printStackTrace(); } } } }
7 驱动(driver)在JDBC中的角色
JDBC驱动提供了特定厂商对JDBC API接口类的实现。
驱动必须提供java.sql包下面这些类的实现:
Connection, Statement, PreparedStatement, CallableStatement, ResutlSet, Driver
8 PreparedStatement 比 Statement有什么优势?
PreparedStatement是预编译的,性能好。Connection.preparedStatment(sql)方法执行可以获得一个PreparedStatement对象,数据库系统会对sql语句进行预编译,预编译后可以重复用,这样它比
Statement对象生成的查询速度更快。适合多次执行。
优点:执行带参数的sql语句。
性能更好,SQL语句会预编译在数据库系统中,执行计划(索引等)同样会被缓存,允许参数化查。
询,使用预处理语句比普通的查询更快(因为对SQL语句的分析,编译,优化已经在第一次查询的时候完成了)
Statement:不能参数化,通用查询,每次都要编译,再执行。适合一次执行。
PreparedStatement:参数话查询,
CallableStatement:存储过程。
在JDBC应用中,如果你已经是稍有水平开发者,你就应该始终以PreparedStatement代替Statement.也就是说,在任何时候都不要使用Statement.
基于以下的原因:
每一种数据库都会尽最大努力对预编译语句提供最大的性能优化.因为预编译语句有可能被重复调用.所以语句在被DB的编译器编译后的执行代码被缓存下来,那么下次调用时只要是相同的预编译语句就不需要编译,只要将参数直接传入编译过的语句执行代码中(相当于一个函数)就会得到执行.这并不是说只有一个 Connection中多次执行的预编译语句被缓存,而是对于整个DB中,只要预编译的语句语法和缓存中匹配.那么在任何时候就可以不需要再次编译而可以直接执行.而statement的语句中,即使是相同一操作,而由于每次操作的数据不同所以使整个语句相匹配的机会极小,几乎不太可能匹配.
最重要一定:极大的提高安全性:
String sql = "select * from tb_name where name= '"+varname+"' and passwd='"+varpasswd+"'";
如果我们把[' or '1' = '1]作为varpasswd传入进来.用户名随意,看看会成为什么?
select * from tb_name = 'XXX' and passwd = '' or '1' = '1';
把[';drop table tb_name;]作为varpasswd传入进来,则:
而如果你使用预编译语句.你传入的任何内容就不会和原来的语句发生任何匹配的关系.(前提是数据库本身支持预编译,但上前可能没有什么服务端数据库不支持编译了,只有少数的桌面数据库,就是直接文件访问的那些)只要全使用预编译语句,你就用不着对传入的数据做任何过虑.而如果使用普通的statement, 有可能要对drop,;等做费尽心机的判断和过虑.
登录的时候在用户输入框写上:张三'# 然后密码框任意填:njksad
select* from users where username='张三'#' and password='njksad'
上述语句中 ”#“表示注释,那么只有用户名不用密码救能访问DB了。
9 如何避免SQL注入攻击?
下面两条是SQL注入的例子:
select * from school.COURSE where cno='1' and cname='name' or 1 = 1
select * from school.COURSE where cno='1'# and cname='name' or 1 = 1
Statement有一个子类叫做PreparedStatement:
PreparedStatement是Statement的孩子,不同的是,PreparedStatement使用预编译机制,在创建PreparedStatement对象时就需要将sql语句传入,传入的过程中参数要用?替代,这个过程回导致传入的sql被进行预编译,然后再调用PreparedStatement的setXXX将参数设置上去,由于sql语句已经经过了预编译,再传入特殊值也不会起作用了。
而且PreparedStatement使用了预编译机制,sql语句在执行的过程中效率比Statement要高。
之所以PreparedStatement能防止注入,是因为它把单引号转义了,变成了\',这样一来,就无法截断SQL语句,进而无法拼接SQL语句,基本上没有办法注入了。
10 什么时候用CallableStatement? 用来准备CallableStatement的方法是什么?
CallableStatement用来执行存储过程。存储过程是由数据库存储和提供的。存储过程可以接收输入参数,也可以有返回结果。 CallableStatement.prepareCall()。
11 数据库连接池
打开/关闭数据库连接浪费时间,成本高。可以再应用服务器启动的时候建立多个数据库连接并维护在一个池中。
连接池就是一个装满了CONNECTION的容器。
你可以自定义通过drivermanager拿到多少个连接,每拿到一个连接,你可以把这些连接封装到一个集合(LIST,SET等),拿到你觉得适合的个数就可以了,当然每次执行完一次SQL请求,这些连接将会还回连接池,而不是直接还给数据库。
12 java 不可变类
因为不可变的对象默认就是线程安全的,他们一旦创建就不能发生改变
immutable Objects就是那些一旦被创建,它们的状态就不能被改变的Objects,每次对他们的改变都是产生了新的immutable的对象,而mutable Objects就是那些创建后,状态可以被改变的Objects.
举个例子:String和StringBuilder,String是immutable的,每次对于String对象的修改都将产生一个新的String对象,而原来的对象保持不变,而StringBuilder是mutable,因为每次对于它的对象的修改都作用于该对象本身,并没有产生新的对象。
实际上JDK本身就自带了一些immutable类,比如String,Integer以及其他包装类。为什么说String是immutable的呢?比如:java.lang.String 的trim,uppercase,substring等方法,它们返回的都是新的String对象,而并不是直接修改原来的对象。
要写出这样的类,需要遵循以下几个原则:
1)immutable对象的状态在创建之后就不能发生改变,任何对它的改变都应该产生一个新的对象。
2)Immutable类的所有的属性都应该是final的。
3)对象必须被正确的创建,比如:对象引用在对象创建过程中不能泄露(leak)。
4)对象应该是final的,以此来限制子类继承父类,以避免子类改变了父类的immutable特性。
5)如果类中包含mutable类对象,那么返回给客户端的时候,返回该对象的一个拷贝,而不是该对象本身(该条可以归为第一条中的一个特例)
使用Immutable类的好处:
1)Immutable对象是线程安全的,可以不用被synchronize就在并发环境中共享
2)Immutable对象简化了程序开发,因为它无需使用额外的锁机制就可以在线程间共享
3)Immutable对象提高了程序的性能,因为它减少了synchroinzed的使用
4)Immutable对象是可以被重复使用的,你可以将它们缓存起来重复使用,就像字符串字面量和整型数字一样。你可以使用静态工厂方法来提供类似于valueOf()这样的方法,它可以从缓存中返回一个已经存在的Immutable对象,而不是重新创建一个。
缺点是:产生大量垃圾。
因为string是不可变的,所以绝对安全,是线程安全的。StringBuilder实际上自身维护一个char[]数组,append是没有synchronized。StringBuffer的append等很多操作都是带有synchronized的,所以同样线程安全。
所以单线程字符串拼接一般采用StringBuilder,效率高。多线程环境则采用Stringbuffer,虽然安全,但是相对效率会低些。