JavaEE Note [vaynexiao]

重写与重载区别

重写:是指子类继续父类后,不想用父类方法就自己重新写了一个方法,如何判定“重新”呢,即父类与子类的方法的参数个数,参数类型完全一致时视为同一个方法。
重载:一个类中的多个方法的名称相同,参数个数或者参数类型不同,则称为重载方法,简单说就是虽然在同一个类中,两个方法很相似,但终究不是同一个,因为参数个数或者参数类型不同,调用时只能明确调用某一个。

封装,继承,多态

  • 封装:将对象的具体信息隐藏起来,不对外暴露,需要访问时通过提供的方法来操作,简单说:该隐藏隐藏,该暴露暴露。

  • 继承:子类继承父类后,共性的东西子类不用写,全都默认拥有。 通过继承能够复用父类代码,提高开发的效率。

  • 多态:同一个对象 调用同一个方法,呈现出不同的表现。

发生的前提: 继承关系 且  重写方法
父类引用 指向 子类对象 Animal p = new Cat();
java的引用类型有两个,编译类型 运行类型,编译看左边(自动向上转型),运行看右边

class Animal {
    
    
    say("aaaaa...");
}
class Cat extends Animal {
    
    
    say("喵..."); 
}
class Dog extends Animal {
    
    
    say("汪...");
}

String、StringBuilder、StringBuffer

String对象一旦创建之后该对象是不可更改的;
而StringBuilder和StringBuffer的对象是变量,对变量进行操作就是直接对该对象进行更改,
而不进行创建和回收的操作,所以速度要比String快很多。

String:适用于少量的字符串操作的情况
StringBuilder:线程不安全,适用于单线程下在字符缓冲区进行大量操作的情况
StringBuffer:线程安全,适用多线程下在字符缓冲区进行大量操作的情况

==和equals()区别

  • ==:比较的是两个对象内存引用地址是否相等,基本类型只有值,所以直接比较;==不能用于比较没有父子关系的两个类的对象,否则编译报错
  • equals:只能比较引用类型:默认比较的是地址值。如:java.lang.Object@2a139a55,但是很多类重写了规则,String就是比较的字符串内容
    对于引用类型,没有覆写equals时,比较的是地址值(return (this == obj);两个内存地址相等说明就是自己跟自己比,当然true)

String s1 = “Hello”;
String s2 = new String(“Hello”);
s2 = s2.intern();
System.out.println(s1 == s2);
// equals比较的是内容绝对返回true,但==比较值时,String的intern方法检查常量池里是否存在"Hello"
存在,就返回池里的字符串所以不会新建字符串;
不存在,会把"Hello"添加到字符串池中,然后再返回它的引用。所以也是true

String s1 = “Hello”;
String s2 = “hell”;
String s3 = “o”;
String s4 = s2 + s3;
System.out.println(s1 == s4); // s5不能编译时就确定内容,所以引用值不同,即false

ps: “hello”.equals(str); //建议写法,防止str是null抛出异常

Object

Object clone()
    要通过clone方法复制某一个对象,在该类中必须实现java.lang.Cloneable接口
    使用等号只是拷贝对象的引用并不是对象,需要拷贝对象的时候,可以借助clone方法。
    clone方法并不会复制一个新的成员变量,而是共用一个对象

int	hashCode()
boolean	equals(Object obj)
	指示其他某个对象是否与此对象“相等”
	
Class<?> getClass()
	返回此 Object 的类。

void notify()
void notifyAll()
void wait()

String toString()

反射

将class文件读取到内存中的过程,称为类加载
class文件的加载由ClassLoader完成,称为类加载器
AppClassLoader:系统类加载器,主要负责加载classpath路径下的类(对于idea就是项目路径/bin目录)
class文件读取到内存中会被封装成 java.lang.Class 类的对象

反射 reflection
对于任意一个类,都能够获取这个类的所有属性和方法
对于任意一个对象,都能够调用这个对象的任意一个属性和方法

Class 表示运行中的类和接口
Field 表示类中的属性
Method 表示类中的方法
Constructor 表示类中的构造方法
Package 表示类所属的包
Modifier 表示修饰符
Parameter 表示方法的参数
Annotation 表示注解

Class对象
运行中的class文件通过Class对象来表示的
- Class对象是在类加载时由JVM自动创建的,一个类在JVM中只会有一个Class对象
- Class类没有公共构造方法,不能自己创建Class对象,但可以获取其实例并进行操作
Class是反射的核心类,要想操作类中的属性和方法,都必须从获取Class对象开始
获取Class对象
- 调用对象的getClass()方法 Class cls = p.getClass()
- 调用类的class属性 Class cls = Person.class
- 调用Class类的forName()静态方法 Class cls = Class.forName(“全名.Person”);

api

Person p = new Person();
Class cls = p.getClass();
System.out.println("类全名:"+cls.getName());
System.out.println("简单类名:"+cls.getSimpleName());
System.out.println("是否为接口:"+cls.isInterface());
Class  superClass  =  cls.getSuperclass();
System.out.println("父类:"+superClass.getSimpleName());
Class[]  interfaces  =  cls.getInterfaces();
System.out.print("实现的接口:");
for  (Class  c  :  interfaces)  {
    
    
    System.out.print(c.getSimpleName()+"\t");       
}
int  modifiers  =  cls.getModifiers(); // 获取修饰符
System.out.println("修饰符:"+ Modifier.toString(modifiers));
System.out.println(cls.getPackage());
try  {
    
    
    Object  obj=cls.newInstance(); // 调用无参构造方法创建对象
    System.out.println(obj);       
}  catch  (InstantiationException  e)  {
    
    
    System.out.println("实例化异常:"+e.getMessage());       
}  catch  (IllegalAccessException  e)  {
    
    
    System.out.println("非法访问异常:"+e.getMessage());       
}

操作Field

Class cls = s.getClass();
//1.获取当前类及父类中所有public修饰的属性
Field[] fields = cls.getFields();
//2.获取当前类中所有的属性,包含private修饰的
fields = cls.getDeclaredFields();
for (Field f : fields) {
    
      // 属性名、属性类型、修饰符
    System.out.println(f.getName()+"\t"+f.getType()+"\t"+Modifier.toString(f.getModifiers() ));
}
//3.获取当前类及父类中指定的public修饰的属性
Field field = cls.getField("sex");
//4.获取当前类中指定的属性
field = cls.getDeclaredField("height");
System.out.println(field);
// 5.通过反射为属性赋值
Student stu = new Student();
field.setAccessible(true); // 取消权限控制检查,访问属性时忽略访问控制符(破坏了封装原则)
field.set(stu, 180.5); //为stu对象的field属性赋值为180.5
// 6.通过反射获取属性值
Object value = field.get(stu); //获取stu对象的指定field属性
System.out.println(value);

操作Method

// 1.获取当前类及父类中所有的public修饰的方法
Method[] methods = cls.getMethods();
// 2.获取当前类中所有的方法,包含private修饰的
methods = cls.getDeclaredMethods();
for (Method m : methods) {
    
    
    // 方法名、返回值类型、修饰符、参数列表
    System.out.println(m.getName() + "\t" + m.getReturnType() + "\t"    
                       + Modifier.toString(m.getModifiers()) + "\t"            
                       + Arrays.toString(m.getParameters()));            
}
// 3.获取当前类及父类中指定的public修饰的方法
Method m = cls.getMethod("c", int.class, int.class);
// 4.获取当前类中指定的方法
m = cls.getDeclaredMethod("c", int.class, double.class);
System.out.println(m);
// 5.通过反射调用方法
Student stu = new Student();
m.setAccessible(true);
Object value = m.invoke(stu, 4, 3.5); // 调用stu对象的method方法,传入参数4和3.5
System.out.println(value);

操作Constructor

Class cls=Student.class;
// 1.获取所有public修饰的构造方法
Constructor[] constructors = cls.getConstructors();
// 2.获取所有的构造方法
constructors = cls.getDeclaredConstructors();
for (Constructor c : constructors) {
    
    
    System.out.println(c);    
}
// 3.获取指定的public修饰的构造方法
Constructor c = cls.getConstructor(String.class,String.class,double.class);
// 4.获取指定的构造方法
c = cls.getDeclaredConstructor(String.class);
System.out.println(c);
// 5.通过反射调用构造方法,创建对象
c.setAccessible(true);
Object obj = c.newInstance("male");
System.out.println(obj);
// 6.直接调用Class对象的newInstance()方法创建对象
Object obj2 = cls.newInstance();
System.out.println(obj2);

注解

    @Retention(RetentionPolicy.RUNTIME)
    // SOURCE 注解只保留在源文件中,不参与编译,即class文件中没有
    // CLASS  注解参与编译,保留在class文件中,但类加载时不加载,即程序运行时没有
    // RUNTIME	注解可以保存在程序运行期间,可以通过反射获取该注解
    @Target({
    
    ElementType.TYPE, ElementType.FIELD, ElementType.METHOD, ElementType.PARAMETER})
    @Documented // 当使用javadoc生成API文档时包含该注解信息
    @Inheried // 允许子类继承父类中的注解
    public @interface CherryAnnotation {
    
    

        // 抽象方法,必须是无参无异常
        String a();	
        int b();

        // 返回数组类型时,使用大括号{}
        String[] c();

        // 使用default关键字为方法指定默认的返回值,在使用注解时可以不指定该属性
        String d() default "ddd";

        // 推荐使用value()名称,在赋值时如果只为该属性赋值,则可以省略value=
        String value() default "defaultValue";
    }
    public class Student {
    
    
        @CherryAnnotation(a="cherry", c={
    
    "1","2"}, value="456")
        public void study(int times){
    
    
            for(int i = 0; i < times; i++){
    
    
                System.out.println("Good Good Study, Day Day Up!");
            }
        }
    }
        Annotation[] annotations = cls.getAnnotations(); // 获取所有注解
        Annotation annotation = cls.getAnnotation(MyAnnotation.class);

        // 获取属性上的注解
        Field field = cls.getDeclaredField("name");
        annotations = field.getAnnotations();
        annotation = field.getAnnotation(MyAnnotation.class);

        // 获取方法上的注解
        Method method = cls.getDeclaredMethod("show", int.class);
        annotations = method.getAnnotations();
        annotation = method.getAnnotation(MyAnnotation.class);

        for (Annotation a : annotations) {
    
    
            System.out.println(a);
        }
        System.out.println(annotation);

        // 通过反射获取注解的属性值
        if(annotation instanceof MyAnnotation){
    
    
            MyAnnotation ma=(MyAnnotation) annotation;
            System.out.println(ma.d());
            System.out.println(ma.value());
        }

        // 判断是否使用了指定的注解
        System.out.println(method.isAnnotationPresent(SuppressWarnings.class));
	public User(){
    
    
		init();
	}
	// 初始化
	private void init() {
    
    
		/*
		 * 加载属性文件
		 */
		Properties p = new Properties();
		try {
    
    
			p.load(User.class.getClassLoader().getResourceAsStream("info.properties"));
		} catch (IOException e) {
    
    
			e.printStackTrace();
		}
		/*
		 * 获取类中所有属性上的@Value注解,并解析处理
		 */
		// 获取所有属性
		Class<? extends User> cls = this.getClass();
		Field[] fields = cls.getDeclaredFields();
		// 循环获取每个属性上的@Value注解
		for (Field field : fields) {
    
    
			Value annotation = field.getAnnotation(Value.class);
			// 判断是否有该注解
			if(annotation!=null){
    
    
				// 获取注解上的值,并对值进行判断处理
				String value = annotation.value();
				if(value.startsWith("${")&&value.endsWith("}")){
    
    
					String key = value.substring(2, value.length()-1);
					value = p.getProperty(key);
				}
				// 为类中的属性赋值
				try {
    
    
					field.setAccessible(true);
					field.set(this, value);
				} catch (IllegalArgumentException e) {
    
    
					e.printStackTrace();
				} catch (IllegalAccessException e) {
    
    
					e.printStackTrace();
				}
			}
		}
	}

JDBC

基础用法

    //准备好mysql的表,数据库叫today,账号root,密码root
	CREATE TABLE `tb_data` (
	  `id` int(11) DEFAULT NULL,
	  `user_name` varchar(100) DEFAULT NULL,
	  `create_time` datetime DEFAULT NULL,
	  `random` double DEFAULT NULL
	) ENGINE=MyISAM DEFAULT CHARSET=utf8;
 
    //testStatement();
    //testPreparedStatement("faker","888888",131);
    //testQuery(120);
    }
//测试Statement执行insert
public static void testStatement() throws SQLException, ClassNotFoundException {
    
    
    //1 加载驱动,各个数据库不一样
    Class.forName("com.mysql.jdbc.Driver"); 
    //2,DriverManager是驱动类,通过静态方法getconnection加载的驱动与数据库建立连接
    Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/today","root","root");
    //oracle - "jdbc:oracle:thin:@//127.0.0.1:1521/orcl";
    //3,创建Statement 发送SQL语句
    Statement state=conn.createStatement();
    //  1:int executeUpdate(String sql)专门用来执行DML语句的方法, 
    //  2:ResultSet executeQuery(String sql)专门用来执行DQL语句的方法
    //  3:boolean execute(String sql)可以执行所有类型的SQL语句,但通常来执行DDL语句。       
    int update = state.executeUpdate("insert into user values('66','66','68')");//增删改用executeUpdate
    conn.close();//一定要关闭资源
    System.out.println(update);
    //使用PreparedStatement 解决SQL注入问题
}
//测试PreparedStatement执行insert(可动态传值)
public static void testPreparedStatement(String username,String password,int id) throws SQLException, ClassNotFoundException {
    
    
    Class.forName("com.mysql.jdbc.Driver");
    Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/today","root","root");
    String sql = "insert into user values(?,?,?)";
    PreparedStatement ps = conn.prepareStatement(sql);//prepareStatement()
    ps.setString(1, username);  //为第一个问号赋值
    ps.setString(2, password);  //为第二个问号赋值
    ps.setInt(3, id);  //为第三个问号赋值
    int i = ps.executeUpdate();  //插入 更新 删除都是用executeUpdate(),返回int表示受影响的行数
    System.out.println(i);
       conn.close();//一定要关闭资源
String sql = "select * from user where username = ? and password = ?";
PreparedStatement pstmt = con.prepareStatement(sql);
 
pstmt.setString(1, username);
pstmt.setString(2, password); //PreparedStatement 可防止SQL注入
}
	//测试executeQuery ResultSet
	public static void testResultSet(int args_id) throws SQLException, ClassNotFoundException {
    
    
	    Class.forName("com.mysql.jdbc.Driver");
	    Connection conn = DriverManager.getConnection("jdbc:mysql://localhost:3306/today","root","root");
	    Statement state = conn.createStatement();
	    String sql = "select * from user where id="+args_id;
	        conn.setAutoCommit(false);  //  默认ture提交事务,可改为不提交事务
	    ResultSet rs = state.executeQuery(sql);
	    while(rs.next()) {
    
    
	    int id = rs.getInt("id");//方式1:表字段为id,get也要写id,注意接收类型
	    String username = rs.getString(1);//方式2:get第几个字段,索引从1开始
	    String password= rs.getString(2);
	    System.out.println(id+" : "+username+" : "+password);
	}
	conn.commit();
	conn.close();//一定要关闭资源


关闭连接
连接之前会自动关闭ps(preparedstatement),关闭ps之前也会自动关闭rs(resultset结果集,)所以只需要关闭conn即可.

缺点:
1、 数据库链接创建、释放频繁造成系统资源浪费从而影响系统性能,如果使用数据库链接池可分批执行节省性能。

2、 Sql语句写在代码中造成代码不易维护,实际应用sql变化的可能较大,sql变动需要改变java代码。

3、 向sql语句传参数麻烦,因为sql语句的where条件不一定,可能多也可能少,占位符需要和参数逐一对应。

4、对结果集解析麻烦,sql变化导致解析代码变化,且解析前需要遍历,如果能将数据库记录封装成pojo对象解析比较方便。

分页

select * from user limit (页数-1)*每页数量,每页数量; --比如每页10条,查出11-15条 就是:105
或者
一个指定的页数pageIndex,和每页显示的数量pageSize,
传给sql中where rownum <= endIndex and rownum > startIndex
int startIndex = (pageIndex - 1) * pageSize; 
int endIndex = (pageIndex * pageSize);

PreparedStatement和Statement 区别

1.可读性: statement相比prepareStatement在使用时候的书写麻烦很多。
2.prepareStatement防止注入攻击:
3.preparedStatement预编译,批处理: 会预编译,即相同的查询语句(不包括其中的数据)会被储存再缓存中下次使用相同语句会直接调用(有点类似调用java中的函数),这样省去大量的编译过程,达到效率最大化:而相反在statement: 面对相同查询语句时,哪怕只有一个int数据不同,都会重新编译
4. 语法区别: 创建PreparedStatement时就将需要预编译得SQL语句传入(实际上在创建statement时会将该SQL语句发送给数据库以生成执行计划)然后设置型参问号的具体数据;而创建statement时,不需要传入sql语句,而是执行sql语句时传入 state.execute(sql);

ResultSet结果集

ResultSet rs = state.executeQuery(sql);
 // boolean next();判断结果集是否还有下一条记录,若有则返回true,
 // 且提供了若干的getXXX(String colName)方法,根据字段名字获取该字段对应的值,不同字段类型使用不同方法
while(rs.next()){
    
    
    int id = rs.getInt("id");
    String username = rs.getString("username");
    String password = rs.getString("password");
    System.out.println(id+","+username+","+password);
}
 * 获取结果结元数据
ResultSetMetaData rsmd = rs.getMetaData();
 * 查看结果集的字段数量
int count = rsmd.getColumnCount();
for(int i=1;i<=count;i++){
    
    
 * 查看指定字段的字段名
String colName = rsmd.getColumnName(i);
System.out.println(colName);

addBatch() executeBatch()

conn.setAutoCommit(false);//先取消事务,控制自动提交
String sql=" select * from emp ";
PreparedStatement ps=conn.prepareStatement(sql);
long start=System.currentTimeMillis();
for(int i=0;i<1000;i++){
    
    
ps.setString(1,"Hello");
//添加到本地缓存中,(添加到批量中),statement每一条sql语句都会生成执行计划,
ps.addBatch();preparedstament会复用与之前匹配的sql语句对应的执行计划,次数batch, crew()方法则是一次性将所有sql语句发送给数据库执行,因此效率比statement高很多。
}
int[] d=ps.executeBatch();//执行批量操作
//statement每执行一次就提交一次,而preparedStatement是一次性提交所有事务
conn.commit();//手动提交事务
conn.rollback();//回滚事务,所以必须在可能出错的代码家还是那个try catch,这里省略没写
long end=System.currentTimeMillis();
System.out.println("共耗时"+(end-start)+"ms");

批量插入数据到Mysql

//原理:拼接一个sql   insert tablename * values (a,b,c,d…………)
public class InsertData {
    
    
        static Connection conn = null;
        public static void initConn() throws ClassNotFoundException, SQLException {
    
    
            String url = "jdbc:mysql://localhost:3306/study?"
                    + "user=root&password=root&useUnicode=true&characterEncoding=UTF8&useSSL=false&serverTimezone=UTC";
            try {
    
    
                Class.forName("com.mysql.jdbc.Driver");// 1,动态加载mysql驱动
                conn = DriverManager.getConnection(url); //  2,得到连接
            } catch (Exception e) {
    
    
                e.printStackTrace();
            }
        }
        public static String randomStr(int size) {
    
    // 得到一个指定字符长度的随机字符串
            String result = "";
            for (int i = 0; i < size; ++i) {
    
    
                int intVal = (int) (Math.random() * 26 + 97);// 生成一个97~122之间的int类型整数,ASCLL码97-122对应a-z
                result = result + (char) intVal;// 强制转换(char)intVal 将对应的数值转换为对应的字符,并将字符进行拼接
            }
            return result;
        }
        public static void insert(int insertNum) {
    
    
            Long begin = System.currentTimeMillis();
            // sql前缀
            String prefix = " INSERT INTO tb_data (id, user_name, create_time, random) VALUES ";
            try {
    
    
                // 保存sql后缀,StringBuffer是可变字符串,用在此处效率高
                StringBuffer suffix = new StringBuffer();
                // 设置事务为非自动提交
                conn.setAutoCommit(false);
                PreparedStatement pst = conn.prepareStatement(" ");// 3,创建statement
                for (int i = 1; i <= insertNum; i++) {
    
    
                    // 构建sql后缀
                   suffix.append("(" + i +",'"+ randomStr(8)  + "', sysdate(), " + Math.random() + "),");
                }
                // 构建完整sql
                String sql = prefix + suffix.substring(0, suffix.length() - 1); //去掉尾部的逗号
                pst.addBatch(sql);// 添加执行sql
                pst.executeBatch(); // 4,执行
                conn.commit();
                pst.close();
                conn.close();
            } catch (SQLException e) {
    
    
                e.printStackTrace();
            }
            Long end = System.currentTimeMillis();
            System.out.println("插入 "+insertNum+" 条数据,耗时: " + (end - begin)  + " 毫秒");
        }
        public static void main(String[] args) throws SQLException, ClassNotFoundException {
    
    
            initConn();
            insert(10000);
        }
}

连接池(数据源)

dbcp

// commons-dbcp2-2.2.0.jar
// commons-pool2-2.5.0.jar(依赖的jar)
// commons-logging-1.2.jar(依赖的jar)
public class DBCPDataSource {
    
    
    
    private static final String connectionURL=
        "jdbc:mysql://localhost:3306/web01?useUnicode=true&characterEncoding=UTF8&useSSL=false" ;
    private static final String username = "root";
    private static final String password = "root";
    private static BasicDataSource ds;
    
    static {
    
    
        // 原生方式:创建一个list保存多个连接
        // 也可以使用dbcp数据源:初始化dbcp数据源
        ds = new BasicDataSource();
        ds.setDriverClassName("com.mysql.jdbc.Driver");
        ds.setUrl(connectionURL);
        ds.setUsername(username);
        ds.setPassword(password);
        ds.setInitialSize(5); //初始化5个连接
        ds.setMaxTotal(20); //设置使用时连接的max数量
        ds.setMinIdle(3);  //设置空闲连接的min数量,防止特殊情况以备用3个连接
    }
    
    public static Connection getConnection() {
    
    
        try {
    
    
            return ds.getConnection();//通过dbcp得到的链接,不需要归还,直接close就可以,
        } catch (SQLException e) {
    
    
            e.printStackTrace();
        }
        return null;
    }
    public static void close(ResultSet rs,Statement stmt,Connection con) {
    
    
        closeResultSet(rs);
        closeStatement(stmt);
        closeConnection(con);
    }
    public static void close(Statement stmt1,Statement stmt2,Connection con) {
    
    
        closeStatement(stmt1);
        closeStatement(stmt2);
        closeConnection(con);
    }
    private static void closeResultSet(ResultSet rs ) {
    
    
        try {
    
    
            if(rs!=null)rs.close();
        } catch (SQLException e) {
    
    
            e.printStackTrace();
        }
    }
    private static void closeStatement(Statement stmt) {
    
    
        try {
    
    
            if(stmt!=null)
                stmt.close();
        } catch (SQLException e) {
    
    
            e.printStackTrace();
        }
    }
    private static void closeConnection(Connection con) {
    
    
        try {
    
    
            if(con!=null)con.close();//这里会把链接归还给dbcp连接池,并不是真正的断开链接
        } catch (SQLException e) {
    
    
            e.printStackTrace();
        }
    }
    //  使用时方法
    // con = C3P0DataSource.getConnection();
    // DBCPDataSource.close();
    
}

c3p0(与dbcp极为类似)

// c3p0-0.9.5.2.jar
// mchange-commons-java-0.2.11.jar(依赖jar)
public class C3P0DataSource {
    
    
    private static final String connectionURL=
          "jdbc:mysql://localhost:3306/web01?useUnicode=true&characterEncoding=UTF8&useSSL=false";
    private static final String username = "root";
    private static final String password = "root";
    private static ComboPooledDataSource ds ;
    
    static {
    
    
        try {
    
    
            ds = new ComboPooledDataSource();
            ds.setDriverClass("com.mysql.jdbc.Driver");
            ds.setJdbcUrl(connectionURL);
            ds.setUser(username);
            ds.setPassword(password);   
            ds.setInitialPoolSize(5);
            ds.setMaxPoolSize(20);
        } catch (PropertyVetoException e) {
    
    
            e.printStackTrace();
        }
    }
    
    public static Connection getConnection() {
    
    
        try {
    
    
            return ds.getConnection();
        } catch (SQLException e) {
    
    
            e.printStackTrace();
        }
        return null;
    }
    
    public static void close(ResultSet rs, Statement stmt, Connection con) {
    
    
        closeResultSet(rs);
        closeStatement(stmt);
        closeConnection(con);
    }
    public static void close(Statement stmt1, Statement stmt2, Connection con) {
    
    
        closeStatement(stmt1);
        closeStatement(stmt2);
        closeConnection(con);
    }
    private static void closeResultSet(ResultSet rs ) {
    
    
        try {
    
    
            if(rs!=null)rs.close();
        } catch (SQLException e) {
    
    
            e.printStackTrace();
        }
    }
    private static void closeStatement(Statement stmt) {
    
    
        try {
    
    
            if(stmt!=null)
                stmt.close();
        } catch (SQLException e) {
    
    
            e.printStackTrace();
        }
    }
    private static void closeConnection(Connection con) {
    
    
        try {
    
    
            if(con!=null)con.close();//这里会把链接归还给连接池,并不是真正的断开链接
        } catch (SQLException e) {
    
    
            e.printStackTrace();
        }
    }
}

正则基本语法

在线手册
https://tool.oschina.net/uploads/apidocs/jquery/regexp.html
表达式全集

[a] # 指定单个字符,此处表示一定是字母a
[^abc] # 除了abc的单个字母
[a-zA-Z] # a-z或者A-Z的单个字母
[a-c[x-z]] # a-c或者x-z 并集
[a-z&[def]] # a-z且def中的单个字母
[a-z&&[^x-z]] # a-z且不在x-z之间的单个字母
. # 任意单个字符
\d # 数字0-9 实际使用需要考虑转义,一般都多加一个\  \\d 以下其他正则都是如此
\D # [^0-9] \\D
\s # 空白字符 \t \n \x0b \f \r \\s
\S # 非空白  \\S
\w # [a-zA-Z0-9]
\W # 与\w相反
str?  # 指定字符串str出现一次或者一次也没有
str* # str0次到多次
str+ # str一次或多次
str{
    
    n} # str n次
str{
    
    n,} # str 至少n次
str{
    
    n,m} # str n-m次
str.split("\\.","")  # 去除所有单个数字
(.)\\1(.)\\2  # AABB 快快乐乐
(..)\\1  # ABAB 快活快活
(.)\\1+ # 任意单个字符出现多次
字符	描述
\	将下一个字符标记为一个特殊字符、或一个原义字符、或一个向后引用、或一个八进制转义符。
例如,“n”匹配字符“n”。“\n”匹配一个换行符。串行“\\”匹配“\”而“\(”则匹配“(”。

^	匹配输入字符串的开始位置。如果设置了RegExp对象的Multiline属性,^也匹配“\n”或“\r”之后的位置。

$	匹配输入字符串的结束位置。如果设置了RegExp对象的Multiline属性,$也匹配“\n”或“\r”之前的位置。

*	匹配前面的子表达式零次或多次。例如,zo*能匹配“z”以及“zoo”。*等价于{
   
   0,}。

+	匹配前面的子表达式一次或多次。例如,“zo+”能匹配“zo”以及“zoo”,但不能匹配“z”。+等价于{
   
   1,}。

?	匹配前面的子表达式零次或一次。例如,“do(es)?”可以匹配“does”或“does”中的“do”。?等价于{
   
   0,1}。

{n}	n是一个非负整数。匹配确定的n次。例如,“o{
   
   2}”不能匹配“Bob”中的“o”,但是能匹配“food”中的两个o。

{n,}	n是一个非负整数。至少匹配n次。例如,“o{
   
   2,}”不能匹配“Bob”中的“o”,但能匹配“foooood”中的所有o。
“o{
   
   1,}”等价于“o+”。“o{
   
   0,}”则等价于“o*”。

{n,m}	m和n均为非负整数,其中n<=m。最少匹配n次且最多匹配m次。例如,“o{
   
   1,3}”将匹配“fooooood”中的前三个o。
“o{
   
   0,1}”等价于“o?”。请注意在逗号和两个数之间不能有空格。

?	当该字符紧跟在任何一个其他限制符(*,+,?,{n},{n,},{n,m})后面时,匹配模式是非贪婪的。
非贪婪模式尽可能少的匹配所搜索的字符串,而默认的贪婪模式则尽可能多的匹配所搜索的字符串。
例如,对于字符串“oooo”,“o+?”将匹配单个“o”,而“o+”将匹配所有“o”。

.	匹配除“\n”之外的任何单个字符。要匹配包括“\n”在内的任何字符,请使用像“(.|\n)”的模式。

(pattern)	匹配pattern并获取这一匹配。所获取的匹配可以从产生的Matches集合得到,
在VBScript中使用SubMatches集合,在JScript中则使用$0…$9属性。要匹配圆括号字符,请使用“\(”或“\)”。

(?:pattern)	匹配pattern但不获取匹配结果,也就是说这是一个非获取匹配,不进行存储供以后使用。这在使用或字符“(|)”
来组合一个模式的各个部分是很有用。例如“industr(?:y|ies)”就是一个比“industry|industries”更简略的表达式。

(?=pattern)	正向肯定预查,在任何匹配pattern的字符串开始处匹配查找字符串。这是一个非获取匹配,
也就是说,该匹配不需要获取供以后使用。例如,“Windows(?=95|98|NT|2000)”能匹配“Windows2000”中的“Windows”,
但不能匹配“Windows3.1”中的“Windows”。预查不消耗字符,也就是说,在一个匹配发生后,
在最后一次匹配之后立即开始下一次匹配的搜索,而不是从包含预查的字符之后开始。

(?!pattern)	正向否定预查,在任何不匹配pattern的字符串开始处匹配查找字符串。
这是一个非获取匹配,也就是说,该匹配不需要获取供以后使用。
例如“Windows(?!95|98|NT|2000)”能匹配“Windows3.1”中的“Windows”,
但不能匹配“Windows2000”中的“Windows”。预查不消耗字符,也就是说,在一个匹配发生后,
在最后一次匹配之后立即开始下一次匹配的搜索,而不是从包含预查的字符之后开始

(?<=pattern)	反向肯定预查,与正向肯定预查类拟,只是方向相反。
例如,“(?<=95|98|NT|2000)Windows”能匹配“2000Windows”中的“Windows”,但不能匹配“3.1Windows”中的“Windows”。

(?<!pattern)	反向否定预查,与正向否定预查类拟,只是方向相反。
例如“(?<!95|98|NT|2000)Windows”能匹配“3.1Windows”中的“Windows”,但不能匹配“2000Windows”中的“Windows”。

x|y	匹配x或y。例如,“z|food”能匹配“z”或“food”。“(z|f)ood”则匹配“zood”或“food”。
[xyz]	字符集合。匹配所包含的任意一个字符。例如,“[abc]”可以匹配“plain”中的“a”。
[^xyz]	负值字符集合。匹配未包含的任意字符。例如,“[^abc]”可以匹配“plain”中的“p”。
[a-z]	字符范围。匹配指定范围内的任意字符。例如,“[a-z]”可以匹配“a”到“z”范围内的任意小写字母字符。
[^a-z]	负值字符范围。匹配任何不在指定范围内的任意字符。例如,“[^a-z]”可以匹配任何不在“a”到“z”范围内的任意字符。
\b	匹配一个单词边界,也就是指单词和空格间的位置。例如,“er\b”可以匹配“never”中的“er”,但不能匹配“verb”中的“er”。
\B	匹配非单词边界。“er\B”能匹配“verb”中的“er”,但不能匹配“never”中的“er”。
\cx	匹配由x指明的控制字符。例如,\cM匹配一个Control-M或回车符。x的值必须为A-Z或a-z之一。否则,将c视为一个原义的“c”字符。
\d	匹配一个数字字符。等价于[0-9]。
\D	匹配一个非数字字符。等价于[^0-9]。
\f	匹配一个换页符。等价于\x0c和\cL。
\n	匹配一个换行符。等价于\x0a和\cJ。
\r	匹配一个回车符。等价于\x0d和\cM。
\s	匹配任何空白字符,包括空格、制表符、换页符等等。等价于[ \f\n\r\t\v]。
\S	匹配任何非空白字符。等价于[^ \f\n\r\t\v]。
\t	匹配一个制表符。等价于\x09和\cI。
\v	匹配一个垂直制表符。等价于\x0b和\cK。
\w	匹配包括下划线的任何单词字符。等价于“[A-Za-z0-9_]”。
\W	匹配任何非单词字符。等价于“[^A-Za-z0-9_]”。

\xn	匹配n,其中n为十六进制转义值。十六进制转义值必须为确定的两个数字长。例如,“\x41”匹配“A”。“\x041”则等价于“\x04&1”。
正则表达式中可以使用ASCII编码。.

\num	匹配num,其中num是一个正整数。对所获取的匹配的引用。例如,“(.)\1”匹配两个连续的相同字符。

\n	标识一个八进制转义值或一个向后引用。如果\n之前至少n个获取的子表达式,则n为向后引用。否则,如果n为八进制数字(0-7),
则n为一个八进制转义值。

\nm	标识一个八进制转义值或一个向后引用。如果\nm之前至少有nm个获得子表达式,则nm为向后引用。
如果\nm之前至少有n个获取,则n为一个后跟文字m的向后引用。如果前面的条件都不满足,若n和m均为八进制数字(0-7),
则\nm将匹配八进制转义值nm。

\nml	如果n为八进制数字(0-3),且m和l均为八进制数字(0-7),则匹配八进制转义值nml。

\un	匹配n,其中n是一个用四个十六进制数字表示的Unicode字符。例如,\u00A9匹配版权符号(©)。

常用正则表达式

//日期的正则表达式
var reg = /^[1-9]\d{
    
    3}-(0[1-9]|1[0-2])-(0[1-9]|[1-2][0-9]|3[0-1])$/;
var regExp = new RegExp(reg);
if(!regExp.test(value)){
    
    
  alert("日期格式不正确,正确格式为:2014-01-01");
  return;
}
//时间的正则表达式
var reg = /^(20|21|22|23|[0-1]\d):[0-5]\d:[0-5]\d$/;
var regExp = new RegExp(reg);
if(!regExp.test(value)){
    
    
  alert("时间格式不正确,正确格式为:12:00:00");
  return;
}
//日期+时间的正则表达式
var reg = /^[1-9]\d{
    
    3}-(0[1-9]|1[0-2])-(0[1-9]|[1-2][0-9]|3[0-1])\s+(20|21|22|23|[0-1]\d):[0-5]\d:[0-5]\d$/;
var regExp = new RegExp(reg);
if(!regExp.test(value)){
    
    
  alert("时间格式不正确,正确格式为: 2014-01-01 12:00:00 ");
  return;
  }
});
24小时制时:分格式 0[0-9]:[0-5][0-9]|1[0-9]:[0-5][0-9]|2[0-3]:[0-5][0-9]
12小时制 时:分格式  0[0-9]:[0-5][0-9]|1[0-1]:[0-5][0-9]
用户名	/^[a-z0-9_-]{
    
    3,16}$/

密码	/^[a-z0-9_-]{
    
    6,18}$/

十六进制值	/^#?([a-f0-9]{
    
    6}|[a-f0-9]{
    
    3})$/

电子邮箱	/^([a-z0-9_\.-]+)@([\da-z\.-]+)\.([a-z\.]{
    
    2,6})$/
/^[a-z\d]+(\.[a-z\d]+)*@([\da-z](-[\da-z])?)+(\.{
    
    1,2}[a-z]+)+$/

URL	/^(https?:\/\/)?([\da-z\.-]+)\.([a-z\.]{
    
    2,6})([\/\w \.-]*)*\/?$/

IP 地址	/((2[0-4]\d|25[0-5]|[01]?\d\d?)\.){
    
    3}(2[0-4]\d|25[0-5]|[01]?\d\d?)/
/^(?:(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\.){
    
    3}(?:25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$/

HTML 标签	/^<([a-z]+)([^<]+)*(?:>(.*)<\/\1>|\s+\/>)$/

删除代码\\注释	(?<!http:|\S)//.*$

Unicode编码中的汉字范围	/^[\u2E80-\u9FFF]+$/

运算符

算数运算符  + - * / % ++ -- += -= *= /=
复制运算符 =
关系运算符 < > >= <= == instanceof
逻辑运算符 && || !
位运算符 & | ^ ~  >> << >>>
	A = 0011 1100
	B = 0000 1101
	A&B 0000 1100 都是11 都是00 否则为0
	A|B 0011 1101 有一个11  否则为0
	A^B 0011 0001 相同0否则1
	~B 1111 0010 每一位取反
	左移 2<<3 16 左移3为就相当于乘以23次方
	0000 0010 这是2
	0001 0000 这是16 效率极其高
	右移 16>>3 2 右移3为就相当于除以23次方
	
条件运算符 ?: 也叫三目运算符 x?y:z x为true取y,否则取z

int a = 10;
int b = 20;
""+a+b //1020
a+b+"" //30

数据类型

https://www.cnblogs.com/HuiH/p/11665880.html
8bit=1字节

8种基本类型 有对应的包装类,包装类都是引用类型,引用类型还有String

6中数字类型(4个整型,2个浮点型)
byte 8bit (-2^7)(2^7-1) 默认值0
short 16bit (-2^15)(2^15-1) 默认值0
int 32bit (-2^31)(2^31-1) 默认值0  
long 64bit (-2^63)(2^63-1) 默认值0 建议:123456789l写成大写L
float 32bit 0.0f 3.14E3就是3.14*10的3次方
double 64bit 0.0d
拓展:涉及精度问题使用BigDecimal
拓展2:jdk新特性 数字之间下划线拼接,不影响值
拓展3:两个int相乘返回long,建议先将其中一个int转为long,再相乘

1个字符型char,默认值'u0000' 转为int就是0,
每个字符转为int都有一个对应数字,unicode表有所有字符对应关系

一个布尔型boolean  默认false

进制
二进制0b开头
十进制
八进制0
十六进制0x 0-9A-F

转义字符
\t制表符
\n换行符

类型转换
int i = 128;
byte b = (byte)i;//强制转换 输出-128 内存溢出了

byte 8bit 转为 int 16bit 自动转
反之转换会损失精度,就必须强制转换

变量

类变量 static 属于类,任何地方随意使用
实例变量 没有static,属于对象,在方法外部,没有初始化也会有默认值
方法变量(局部变量) 在方法中

常量 final 不可变,一般变量名全大写,下划线拼接
static final PI = 3.14;  staticfinal都是修饰符,不分前后

对象初始化顺序

但如果继续创建一个同类对象就不在执行 “静态变量 和 静态初始化块”,只执行后两者

父类静态变量、父类静态初始化块、
子类静态变量、子类静态初始块、
父类变量、父类初始化块、父类构造函数、
子类变量、子类初始化块、子类构造函数。

构造器

默认有无参构造;
如果存在有参构造,就必须显示定义无参

对象引用 栈堆(谐音:战队)

栈内存保存引用地址,堆内存保存对象的内容,即属性和属性值
在这里插入图片描述
引用传递
在这里插入图片描述
产生垃圾
在这里插入图片描述

异常分类

  • 检查异常:(必须抛出)
    所有继承自Exception类的异常,称为检查异常 Checked Exception
    该类异常是可预期的,很有可能会发生
    编译器要求必须显式处理该异常,即编写代码时就强制要处理
  • 运行时异常(可以不抛出,运行时才有可能发生)
    所有继承自RuntimeException类的异常,称为运行时异常
    该类异常并是否发生不可预测
    如果代码没有逻辑性错误,是不会出现运行时异常
    编译器不要求必须处理该异常,即编写代码时可以不处理

异常的产生和处理

  1. 产生
    每种异常都是使用一个Java类来表示
    异常的产生:
    1. 当程序发生异常时,会自动生成一个对应异常类的对象,然后将该异常对象提交给JRE,这个过程称为抛出异常 throw
    2. 当JRE接收到异常对象时,会寻找能处理此异常的代码并把当前异常对象交给其处理,这个过程称为捕获异常 catch
    3. 如果JRE找不到可以捕获异常的代码,则系统终止,程序将退出所以需要对异常进行处理,否则程序将立即终止,无法继续执行。
  2. 处理
    异常处理的两种方式:两种方式效果相同,只是手动抛出的信息更相信,建议手动抛出
  • 2.1 使用try…catch
    使用try…catch…finally捕获并处理异常
    语法:
 /*
  * 捕获多种异常时
  * 1.应将范围小的异常放到前面
  * 2.当匹配到一个catch时,将不再向下继续查找其他catch
  */
try{
    
    
    //可能出现异常的代码
    // return;  // finally依然会执行,实际上try里面如果出现了异常,try中后续语句不会执行
    // System.exit(0); // 终止当前正在运行的JVM,finally不会再执行
}catch(   ClassCastException | NumberFormatException e 异常类型 异常对象){
    
     
    //对异常进行处理的代码
    //e.printStackTrace();打印堆栈中的异常详细信息,通过单独线程进行打印的,所以在控制台打印的位置不确定,使用System.err.println();
    System.out.println(e.getMessage()); //获取异常的消息字符串,即导致产生异常的变量中的字符串
}finally{
    
    
    //无论是否出现异常都必须要执行的代码
}

注意:
try是必须的,catch和finally至少要有一个
catch可以有多个,用来捕获多个不同类型的异常,必须先捕获小异常,再捕获大异常

使用throws声明抛出异常
如果一个方法可能会产生某种异常,但并不知道如何处理这种异常,此时可以声明该方法会抛出异常,表明该方法将不对这种异常进行处理,而由该方法的调用者来处理

使用throws和throw关键字:
throws用来声明方法中会抛出异常,说白了就是表示自己不处理,哪个方法调用就由它来处理,一直往上层抛,没人管就由JVM抛出,即控制台打印错误信息(在方法声明处:throws ParseException, ClassNotFoundException)
throw用来在方法内手动抛出异常

throws出现在方法函数头;而throw出现在函数体。

    /*
     * 除法运算
     */
    public static int divide(int num1,int num2) throws ArithmeticException{
    
    
        if(num2==0){
    
    
            // ArithmeticException e=new ArithmeticException("除数不能为零");
            // throw e;
            throw new ArithmeticException("除数不能为零"); // 使用throw手动抛出异常
        }
        return num1/num2;
    }

自定义异常

自定义异常类时,需要继承Exception类或其子类
一般多继承自Exception或RuntimeException

  • 如果继承Exception,则为检查异常,必须处理
  • 如果继承RuntimeException,则为运行时异常,可以不处理
public class Test05_自定义异常 {
    
    
    public static void main(String[] args) {
    
    
        Scanner input = new Scanner(System.in);
        System.out.print("请输入用户名:");
        String username = input.next();
        System.out.print("请输入密码:");
        String password = input.next();
        // 调用注册方法,实现注册功能
        try {
    
    
            register(username, password);
            System.out.println("注册成功!");
        } catch (UsernameExistException e) {
    
    
            System.out.println("注册失败:"+e.getMessage());
        }
    }
    // 用户注册
    public static void register(String username, String password) throws UsernameExistException {
    
    
        if ("admin".equals(username) || "tom".equals(username)) {
    
    
            throw new UsernameExistException("用户名已存在");
        }
        // 将用户名和密码保存到数据库中
    }
}
 
// 自定义异常类
class UsernameExistException extends Exception {
    
    
    public UsernameExistException() {
    
    
        super();
    }
    public UsernameExistException(String message, Throwable cause,
            boolean enableSuppression, boolean writableStackTrace) {
    
    
        super(message, cause, enableSuppression, writableStackTrace);
    }
    public UsernameExistException(String message, Throwable cause) {
    
    
        super(message, cause);
    }
    public UsernameExistException(String message) {
    
    
        super(message);
    }
    public UsernameExistException(Throwable cause) {
    
    
        super(cause);
    }
}

方法重写的异常问题

方法重写时的异常问题

  • 若父类不抛出异常,则子类不能抛出检查异常 ,但可以抛出运行时异常或在方法内部使用try…catch捕获处理异常
  • 若父类抛出异常,子类可以不抛出异常
  • 重写方法不能抛出比被重写方法范围更大的异常类型
class A {
    
    
   public void show() throws Exception{
    
    
       System.out.println("A.show()");
   }
}
class B extends A {
    
    
   @Override
   public void show() throws ClassNotFoundException{
    
    
       System.out.println("B.show()");
       Class.forName("java.lang.String");  
   }
}

异常的定位和解决

查找异常的出现的位置并解决:

  1. 首先查看有没有Caused by,如果有则从Caused by开始找,如果没有则从头开始找
  2. 然后找到第一行自己写的代码,问题就在这里
  3. 最后根据Caused by或第一行的 所在行的异常类型和异常消息 ,确定产生异常的原因
public class Test07_异常的定位和解决 {
    
    
    public static void main(String[] args) throws DateConvertException  {
    
    
        ClassA a = new ClassA();
        a.a();
    }
}
class ClassA {
    
    
    public void a() throws DateConvertException {
    
    
        System.out.println("ClassA.a()");
        ClassB b = new ClassB();
        b.b();
    }
}
class ClassB {
    
    
    public void b() throws DateConvertException {
    
    
        System.out.println("ClassB.b()");
        ClassC c = new ClassC();
        try {
    
    
            c.c();
        } catch (ParseException e) {
    
    
            // 进行异常转换,转换为自定义的异常
            throw new DateConvertException("日期转换出现异常:" + e.getMessage(), e); 
                   // 传入异常对象,表示异常发生的原由
        }
    }
}
class ClassC {
    
    
    public void c() throws ParseException {
    
    
        System.out.println("ClassC.c()");
        Date date = new SimpleDateFormat("yyyy-MM-dd").parse("2019/2/14");
        System.out.println(date);
    }
}

异常转换

try {
    
    
    c.c();
} catch (ParseException e) {
    
    
    // 进行异常转换,转换为自定义的异常
    throw new DateConvertException("日期转换出现异常:" + e.getMessage(),  e); 
    // 传入异常对象,表示异常发生的原由
}

枚举7种常见的用法

用法一:常量

public enum Color {
    
      
  RED, GREEN, BLANK, YELLOW  
}  

用法二:switch case

enum Signal {
    
      
    GREEN, YELLOW, RED  
}  
public class TrafficLight {
    
      
    Signal color = Signal.RED;  
    public void testSwitch() {
    
      
        switch (color) {
    
      
        case RED:  
            color = Signal.GREEN;  
            break;  
        case YELLOW:  
            color = Signal.RED;  
            break;  
        case GREEN:  
            color = Signal.YELLOW;  
            break;  
        default:
                System.out.println("default");
        }  
    }  
}  

用法三:向枚举中添加新方法

// 如果打算自定义自己的方法,那么必须在enum实例序列的最后添加一个分号。而且 Java 要求必须先定义 enum 实例。
public enum Color {
    
      
    RED("红色", 1), GREEN("绿色", 2), BLANK("白色", 3), YELLO("黄色", 4);   
    private String name;  
    private int index;  
    // 普通方法  
    public static String getName(int index) {
    
      
        for (Color c : Color.values()) {
    
      
            if (c.getIndex() == index) {
    
      
                return c.name;  
            }  
        }  
        return null;  
    }   
}  

用法四:覆盖枚举的方法

下面给出一个toString()方法覆盖的例子。

public enum Color {
    
      
    RED("红色", 1), GREEN("绿色", 2), BLANK("白色", 3), YELLO("黄色", 4);  
    // 成员变量  
    private String name;  
    private int index;  
    // 构造方法  
    private Color(String name, int index) {
    
      
        this.name = name;  
        this.index = index;  
    }  
    //覆盖方法  
    @Override  
    public String toString() {
    
      
        return this.index+"_"+this.name;  
    }  
}

用法五:实现接口

所有的枚举都继承自java.lang.Enum类。由于Java 不支持多继承,所以枚举对象不能再继承其他类。

public interface Behaviour {
    
      
    void print();  
    String getInfo();  
}  
public enum Color implements Behaviour{
    
      
    RED("红色", 1), GREEN("绿色", 2), BLANK("白色", 3), YELLO("黄色", 4);  
    // 成员变量  
    private String name;  
    private int index;  
    // 构造方法  
    private Color(String name, int index) {
    
      
        this.name = name;  
        this.index = index;  
    }  
//接口方法  
    @Override  
    public String getInfo() {
    
      
        return this.name;  
    }  
    //接口方法  
    @Override  
    public void print() {
    
      
        System.out.println(this.index+":"+this.name);  
    }  
}  

用法六:使用接口组织枚举

public interface Food {
    
      
    enum Coffee implements Food{
    
      
        BLACK_COFFEE,DECAF_COFFEE,LATTE,CAPPUCCINO  
    }  
    enum Dessert implements Food{
    
      
        FRUIT, CAKE, GELATO  
    }  
}  

用法七:关于枚举集合的使用

java.util.EnumSet和java.util.EnumMap是两个枚举集合。
EnumSet保证集合中的元素不重复;
EnumMap中的 key是enum类型,而value则可以是任意类型。

测试代码

public class Test {
    
    
    public static void main(String[] args) {
    
    
        for (SeasonEnum seasonEnum : SeasonEnum.values()) {
    
    
            System.out.println(seasonEnum + "  在枚举类中出现的序号ordinal:" + seasonEnum.ordinal()+"  "+seasonEnum.name());
        }
        for (TypeEnum type : TypeEnum.values()) {
    
    
            System.out.println("type:" + type + "    name():" + type.name() + "    getAge():" + type.getAge() + "   ordinal():" + type.ordinal());
        }

        String age = "103";
        TypeEnum type = TypeEnum.fromAge(age);
        if (TypeEnum.BALANCE.equals(type)) {
    
    
            System.out.println("根据字符串获得的枚举类型实例跟枚举常量一致");
        } else {
    
    
            System.out.println("未找到匹配值");
        }
    }

    private enum SeasonEnum {
    
     // 季节枚举
        SPRING,
        SUMMER,
        AUTUMN,
        WINTER
    }
    private enum TypeEnum {
    
     // 带参数的枚举常量
        FIREWALL("101"),
        SECRET("102"),
        BALANCE("103");
        private String age;
        TypeEnum(String typeName) {
    
    
            this.age = typeName;
        }
        public static TypeEnum fromAge(String age) {
    
     // 根据类型的名称,返回类型的枚举实例
            for (TypeEnum type : TypeEnum.values()) {
    
    
                if (type.getAge().equals(age)) {
    
    
                    return type;
                }
            }
            return null;
        }
        public String getAge() {
    
    
            return this.age;
        }
    }
}
枚举类型对象之间的值比较,是可以使用==,直接来比较值,是否相等的,不是必须使用equals方法
因为在Enum类里面,已经重写了equals方法,而方法里面比较就是直接使用==,来比较2个对象的

这个枚举,他是个对象,就像你定义的Student类,Person类,等等一些个类一样。
要有这么个概念。只要是个类,他就可以有构造函数,可以有属性,可以有方法。
有2个默认的属性,一个是name,一个是ordinal,这2个属性就像你定义Student类和Person类的name和age一样,
只不过,这2个是系统自带的属性,不用你自己去定义啦。
你也可以给这个枚举类,也就是你自己声明的枚举,随便加属性。
不能对系统自带的name属性,在构造函数里面赋值,没有为什么。

补充一点
就是这个枚举类型,一旦创建,且被使用(比如,存数据库啥的)之后,持久化后的对象信息里面就保存了这个枚举信息。这个时候你的需求或者要求啥的,需要变更这个枚举名称。**应当禁止这个变更的操作,只能重新创建,用新的代替旧的,不能直接把旧的给改了**,因为,就数据在逆转成对象的时候,如果,旧的枚举不在了,那么就会400还是500的报错或者是空指针的bug。这也是需要关注的一个问题。希望注意下,不然等到出bug了再想到这个问题,就不好了。

Scanner

public class TestShunxu {
    
    
    public static void main(String[] args) {
    
    
        /**
         * next()与nextLine()比较:
         * next():
         *   1、一定要读取到有效字符后才可以结束输入。
         *   2、对输入有效字符之前遇到的空白,next() 方法会自动将其去掉。
         *   3、只有输入有效字符后才将其后面输入的空白作为分隔符或者结束符。
         *   4、next() 不能得到带有空格的字符串。
         * nextLine():
         *   1、以Enter为结束符,也就是说 nextLine()方法返回的是输入回车之前的所有字符。
         *   2、可以获得空白。
         */
        Scanner sc = new Scanner(System.in);
        if ( sc.hasNext() ) {
    
    
            // 此时输入“hello world”
            String str = sc.next();
            System.out.println(str);// hello
        }
        Scanner sc2 = new Scanner(System.in);
        if ( sc.hasNextLine() ) {
    
    
            // 此时输入“hello world”
            String str2 = sc2.nextLine();
            System.out.println(str2);// hello world
        }

        Scanner sc3 = new Scanner(System.in);
        int sum = 0;
        while ( sc3.hasNextInt() ) {
    
     // 此处死循环,但输入的不是int时就自动结束
            int i = sc3.nextInt();
            sum += i;
        }
        System.out.println("总和:"+sum);

        //还有 nextInt nextFloat nextDouble nextLong
        //要注意:if中使用什么方法在sc点的时候也用同样的方法,不然牛客网 leetcode等网站时测试用例不通过
        sc.close();//规范化最好关闭
    }
}

switch

       // switch表达式支持byte short int char string
        // case穿透,不写break下面的case仍然会走下去
        int i = 0;
        switch ( i ) {
    
    
            case 1:
                System.out.println(111);
                break;
            case 2:
                System.out.println(222);
                break;
            case 3:
                System.out.println(333);
                break;
            case 4:
                System.out.println(444);
                break;
            default:
                System.out.println("no founddddddddddddddddd");
        }

可变参数

如果该方法除了可变参数还有其它的参数,可变参数必须放到最后;
调用使用了可变参数的方法时:
a. 可以不写参数,即传入空参;
b. 可以直接在里边写入参数,参数间用逗号隔开;
c. 可以传入一个数组;
拥有可变参数的方法可以被重载,在被调用时,如果能匹配到参数定长的方法则优先调用参数定长的方法。
可变参数可以兼容数组参数,但数组参数无法兼容可变参数,所以试图使用数组作为参数去实现重载时,会报错,说明可变参数与数组冲突

//求若干个整型数中的最大值
public int getMax(int... items){
    
           //定义可变参数items
    int max = Integer.MIN_VALUE;     
    for(int item : items){
    
    
        max = item > max? item : max;    //取大值
    }
    return max;
}

Arrays

String[] stringArray = {
    
     "a", "b", "c", "d", "e" };  
ArrayList<String> arrayList = new ArrayList<String>(Arrays.asList(stringArray));  
System.out.println(arrayList);// [a, b, c, d, e]
	
int[] array1 = new int[]{
    
    1, 2, 3, 4};
int[] array2 = new int[]{
    
    1, 2, 3, 4};
boolean b1 = Arrays.equals(array1, array2); //true 判断两个数组是否相等

int[] array1 = new int[]{
    
    1, 2, 3, 4};
System.out.println(Arrays.toString(array1));
// 输出结果为[1, 2, 3, 4]
// Arrays.deepToString()主要用于数组中还有数组的情况,
// 而Arrays.toString()则相反,对于Arrays.toString()而言,
// 当数组中有数组时,不会打印出数组中的内容,只会以地址的形式打印出来。

int[] array1 = new int[5]; 
Arrays.fill(array1, 1); //给指定数组的每个元素分配指定的值
System.out.println(Arrays.toString(array1));
// 输出结果为[1, 1, 1, 1, 1]
// Arrays.sort(int[] a, int fromIndex, int toIndex) 也可以增加参数指定要分配的值的范围

int[] array = new int[]{
    
    99, 23, 33, 0, 65, 9, 16, 84};
Arrays.sort(array);//按默认升序对指定数组进行排序
System.out.println(Arrays.toString(array));
// 输出结果为[0, 9, 16, 23, 33, 65, 84, 99]

// binarySearch(int[] a, int value):使用二分搜索算法在指定的数组中搜索指定的值,
// 并返回该值所在索引位置;若查询不到,则返回-1
int[] array = new int[]{
    
    1, 17, 20, 44, 45, 62, 79, 88, 93};
int i = Arrays.binarySearch(array, 44);
System.out.println(i);
// 输出结果为3

boolean b = Arrays.asList(stringArray).contains("a");

int[] intArray = {
    
     1, 2, 3, 4, 5 };  
int[] intArray2 = {
    
     6, 7, 8, 9, 10 };  
// Apache Commons Lang 库  
int[] combinedIntArray = ArrayUtils.addAll(intArray, intArray2);

Set<String> set = new HashSet<String>(Arrays.asList(stringArray));  

int[] intArray = {
    
     1, 2, 3, 4, 5 };  
ArrayUtils.reverse(intArray); 

DecimalFormat 转换数字调整精度

  1. 以“0”补位时:

如果数字少了,就会补“0”,小数和整数都会补;

如果数字多了,就切掉,但只切小数的末尾,整数不能切;

同时被切掉的小数位会进行四舍五入处理。

  1. 以“#”补位时:

如果数字少了,则不处理,不会补“0”,也不会补“#”;

如果数字多了,就切掉,但只切小数的末尾,整数不能切;

同时被切掉的小数位会进行四舍五入处理。

public class TestNum {
    
    
    public static String decimalFormat(float value) {
    
    
        DecimalFormat decimalFormat = new DecimalFormat();
        decimalFormat.applyPattern("0.0000");
        return decimalFormat.format(value);
    }

    public static String decimalFormat(double value) {
    
    
        DecimalFormat decimalFormat = new DecimalFormat("0.000000000000000");
        return decimalFormat.format(value);
    }
    public static String decimalFormat(String value) {
    
    
        if (value == null || value.isEmpty()){
    
     return "0.00"; }

        double d = Double.parseDouble(value);
        DecimalFormat decimalFormat = new DecimalFormat();
        decimalFormat.applyPattern("0.##");
        return decimalFormat.format(d);
    }
    public static void main(String[] args) {
    
    
        System.out.println(decimalFormat(2.234234f));//转换float
        System.out.println(decimalFormat(2.234234));//转换double
        System.out.println(decimalFormat("2.234234"));//转换String
        System.out.println(decimalFormat("2.234234f"));//转换String 其实是float f
        System.out.println(decimalFormat("2.234234F"));//转换String 其实是float F
        System.out.println(decimalFormat("2.234234nba"));//转换String 非法数字
    }
}

上面提到的四舍五入,严格来说要比5大一点点才会进1,等于5是不会进的(0.##)

        System.out.println(decimalFormat("2.54500000000000001"));//2.54
        System.out.println(decimalFormat("2.5450000000000001"));//2.54
        System.out.println(decimalFormat("2.545000000000001"));//2.55
        System.out.println(decimalFormat("2.54500000000001"));//2.55
        System.out.println(decimalFormat("2.5450000000001"));//2.55

Timer

利用定时器每过一秒打印1,这我们使用普通语句for循环中间加载一个Thread.sleep()也可以实现,
但区别很大不一样,Java程序是一个线程,而定时器是新开的一个线程,也就是说此时同时两个人在完成我们指定的事情。并不是一个人同时执行两个事情,这就是定时器的本质,启动另一哥新的线程。

精度问题 BigDecimal BigInteger

public class Test {
    
    

    public static void main(String[] args) {
    
    
        
        System.out.println( 0.2 + 0.7 ); // 打印:0.8999999999999999   纳尼?
        // JDK早已为我们考虑到了浮点数的计算精度问题,因此提供了专用于高精度数值计算的大数类来方便我们使用。
        // 注意:大数字指的并不是数值很大
        // Java的大数类位于java.math包下
        // 常用的BigInteger 和 BigDecimal就是处理高精度数值计算的利器。
        // BigInteger处理大数字(整数)
        // BigDecimal处理大数字(小数)

        BigDecimal num3 = new BigDecimal( Double.toString( 1.0f ) );
        BigDecimal num4 = new BigDecimal( Double.toString( 0.99999999f ) );
        System.out.println( num3 == num4 );  // 打印 false

        BigDecimal num1 = new BigDecimal( Double.toString( 0.2 ) );
        BigDecimal num2 = new BigDecimal( Double.toString( 0.7 ) );

        // 加
        System.out.println( num1.add( num2 ) );  // 打印:0.9
        // 减
        System.out.println( num1.subtract( num2 ) );  // 打印:-0.5
        // 乘
        System.out.println( num1.multiply( num2 ) );  // 打印:0.14
        // 除
        System.out.println( num2.divide( num1 ) );  // 打印:3.5  
    }
}

获取当前时间戳

获取当前时间戳

//方法 一
System.currentTimeMillis();
//方法 二
Calendar.getInstance().getTimeInMillis();
//方法 三
new Date().getTime();

获取当前时间

SimpleDateFormat df = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");//设置日期格式
String date = df.format(new Date());// new Date()为获取当前系统时间,也可使用当前时间戳

速度测试

public class Test01 {
    
    
    private static long _TEN_THOUSAND=10000;
    public static void main(String[] args) {
    
    
        long times=1000*_TEN_THOUSAND;
        long t1=System.currentTimeMillis();
        testSystem(times);
        long t2=System.currentTimeMillis();
        System.out.println(t2-t1);//66

        testCalander(times);
        long t3=System.currentTimeMillis();
        System.out.println(t3-t2);//2205

        testDate(times);
        long t4=System.currentTimeMillis();
        System.out.println(t4-t3);//65
        
        //Calendar.getInstance().getTimeInMillis() 这种方式速度最慢
        //这是因为Canlendar要处理时区问题会耗费较多的时间。

    }
    public static void testSystem(long times){
    
    //use 188
        for(int i=0;i<times;i++){
    
    
            long currentTime=System.currentTimeMillis();
        }
    }

    public static void testCalander(long times){
    
    //use 6299
        for(int i=0;i<times;i++){
    
    
            long currentTime= Calendar.getInstance().getTimeInMillis();
        }
    }

    public static void testDate(long times){
    
    
        for(int i=0;i<times;i++){
    
    
            long currentTime=new Date().getTime();
        }
    }
}

long时间戳转换为时间点和时间段

public class Test01 {
    
    
    public static void main(String[] args) {
    
    

        long time = 2147483647;//1m = 1000毫秒  此时time赋值为Integer.MAX_VALUE

        long day = time/(60*60*1000*24);
        long hour = (time-day*60*60*1000*24)/(60*60*1000);
        long minute = (time - day*60*60*1000*24 - hour*60*60*1000)/(60*1000);
        long second = (time - day*60*60*1000*24 - hour*60*60*1000 - minute*60*1000)/1000;

        System.out.println(day + "天" + hour+ "时" + minute + "分" + second+"秒");

        Date date = new Date(time);
        SimpleDateFormat sd = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        System.out.println(sd.format(date));

        //0天0时0分1秒
        //1970-01-01 08:00:01 可以看出起始时间并不是1970-01-01 00:00:00
    }
}

Calendar

        Calendar cal = Calendar.getInstance();
        System.out.println(cal);//这里可以看到Calendar对象中储存了时区 日期 时间等一切有关时间的属性
        System.out.println(cal.get(Calendar.YEAR));//获取年份
        System.out.println(cal.get(Calendar.MONTH)+1);//月份特殊,要加1
        System.out.println(cal.get(Calendar.DATE));//
        System.out.println(cal.get(Calendar.HOUR));
        System.out.println(cal.get(Calendar.HOUR_OF_DAY));
        System.out.println(cal.get(Calendar.MINUTE));
        System.out.println(cal.get(Calendar.SECOND));
        System.out.println(cal.get(Calendar.MILLISECOND));

        cal.add(Calendar.DATE, 7);//增加7天
        System.out.println("增加7天:"+cal.getTime());

        cal.set(2013, 5, 4, 12, 44, 55);
        cal.set(2013, 5, 4, 13, 44);
        cal.set(2013, 5, 4);

        cal.set(Calendar.YEAR, 2020);
        cal.set(Calendar.MONTH, 5);
        cal.set(Calendar.DATE, 13);
        cal.set(Calendar.HOUR_OF_DAY, 14);
        cal.set(Calendar.MINUTE, 30);
        cal.set(Calendar.SECOND, 55);
        cal.set(Calendar.MILLISECOND, 999);

        System.out.println(cal.getTime());
        //这里即便是设置了新的毫秒数,也不会变化,因为cal.getTime()返回的Date对象,只是年月日时分秒,没有毫秒

        System.out.println(new Date().getTime());//Date 使用getTime获得long时间戳

        System.out.println(System.currentTimeMillis());
        //返回当前时间戳,但比上一行方法效率高,因为不存在计算时区等信息,计算更快

fori foreach

public class Test01 {
    
    
    public static void main(String[] args) {
    
    
        ArrayList<Integer> arrayList = new ArrayList<>();
        LinkedList<Integer> linkedList = new LinkedList<>();

        for (int i=0;i<100000;i++){
    
    
            linkedList.add(i);
            arrayList.add(i);
        }

        long one = System.nanoTime();
        for(int i=0;i<arrayList.size();i++){
    
    
            Integer integer = arrayList.get(i);
        }
        long two = System.nanoTime();
        System.out.println(two-one+":以fori的方式循环arraylist");

        long one1 = System.nanoTime();
        for(Integer i: arrayList){
    
    
            Integer integer = arrayList.get(i);
        }
        long two1 = System.nanoTime();
        System.out.println(two1-one1+":以foreach的方式循环arraylist");

        long three = System.nanoTime();
        for(int i=0;i<linkedList.size();i++){
    
    
            Integer integer = linkedList.get(i);
        }
        long four = System.nanoTime();
        System.out.println(four-three+":以fori的方式循环linkedList");

        long three1 = System.nanoTime();
        for(Integer i:linkedList){
    
    
            Integer integer = linkedList.get(i);
        }
        long four1 = System.nanoTime();
        System.out.println(four1-three1+":以foreach的方式循环linkedList");
        /**
         * 701300:以fori的方式循环arraylist
         * 1815300:以foreach的方式循环arraylist
         * 50713100:以fori的方式循环linkedList
         * 46270000:以foreach的方式循环linkedList
         *
         * 在循环以array为底层实现的ArrayList的时候,fori的效率大概是foreach的3-4倍左右。
         * 在循环以连表为底层实现的LinkedList的时候,foreach的效率比fori稍快。
         * 对于不同的底层集合,可以有根据的选择适合的循环方式
         */
    }
}

String -> int / Integer

        String num = "123";
        Integer i21 = Integer.parseInt(num);
        Integer i22 = Integer.valueOf(num);
        System.out.println(i21+"==="+i22);// 当num为空字符串或null时,NumberFormatException
        int i23 = Integer.parseInt(num);
        int i24 = Integer.valueOf(num);
        System.out.println(i23+"==="+i24);// 打印都是123===123

int / Integer -> String

        int i = 1;
        Integer i2 = new Integer(2);

        //1, 强转至String
//       String str1 = (String)i;编译不通过,不可以强转
//       String str2 = (String)i2;编译不通过,不可以强转

        //2, Integer.toString()
        String str3 = Integer.toString(i);
        String str4 = Integer.toString(i2);

        //3, .toString() 只适用包装类Integer
//      String str5 = i.toString();报错  int是基本类型,没有toString()
        String str6 = i2.toString();//调的还是静态方法Integer.toString(),且不可能对null使用,会空指针异常
        
        //4, String.valueOf()
        String str7 = String.valueOf(i); //直接使用String类的静态方法,只产生一个对象
        String str8 = String.valueOf(i2); //直接使用String类的静态方法,只产生一个对象

        //5,追加""
        String str9 = i+""; //会产生两个String对象
        String str10 = i2+""; //会产生两个String对象

Integer(默认缓存127)

		Integer b1 = 12;
		Integer b2 = 12;
		System.out.println(b1==b2);//true
		Integer b3 = 128;
		Integer b4 = 128;
		System.out.println(b3==b4);//false
		//其实Integer这个包装类直接赋值的话默认有一个缓存(-128到127)
		//如果改变值为128,超出了范围,就会底层重新new一个Integer,所以b3==b4为false
		System.out.println( b3.intValue()==b4.intValue() );//true 此时取的是int,所以为true

StringUtils

StringUtils 中一共有130多个方法,并且都是 static 的,所以我们可以这样调用 StringUtils.xxx()

  • String trim(String str)
StringUtils.trim(null) // null
StringUtils.trim("") // ""
StringUtils.trim(" ") // ""
StringUtils.trim(" \b \t \n \f \r    ") // ""
StringUtils.trim("     \n\tss   \b") // "ss"
StringUtils.trim(" d   d dd     ") // "d   d dd"
StringUtils.trim("dd     ") // "dd"
StringUtils.trim("     dd       ") // "dd"
  • String trimToNull(String str)
StringUtils.trimToNull(null) // null
StringUtils.trimToNull("") // null
StringUtils.trimToNull(" ") // null
StringUtils.trimToNull("     \b \t \n \f \r    ") // null
StringUtils.trimToNull("     \n\tss   \b") / "ss"
StringUtils.trimToNull(" d   d dd     ") // "d   d dd"
StringUtils.trimToNull("dd     ") // "dd"
StringUtils.trimToNull("     dd       ") // "dd"
  • String strip(String str)
    去掉字符串两端的空白符(whitespace) ,如果输入为 null 则返回 null
    下面是示例(注意和 trim() 的区别):
StringUtils.strip(null) // null
StringUtils.strip("") // ""
StringUtils.strip(" ") / ""
StringUtils.strip("     \b \t \n \f \r    ") // "\b"
StringUtils.strip("     \n\tss   \b") // "ss   \b"
StringUtils.strip(" d   d dd     ") // "d   d dd"
StringUtils.strip("dd     ") // "dd"
StringUtils.strip("     dd       ") // "dd"
  • String stripToNull(String str)
    去掉字符串两端的空白符(whitespace) ,如果变为 null 或"",则返回 null
    下面是示例(注意和 trimToNull() 的区别):
StringUtils.stripToNull(null) // null
StringUtils.stripToNull("") // null
StringUtils.stripToNull(" ") // null
StringUtils.stripToNull("     \b \t \n \f \r    ") // "\b"
StringUtils.stripToNull("     \n\tss   \b") // "ss   \b"
StringUtils.stripToNull(" d   d dd     ") // "d   d dd"
StringUtils.stripToNull("dd     ") // "dd"
StringUtils.stripToNull("     dd       ") // "dd"
  • String stripToEmpty(String str)
    去掉字符串两端的空白符(whitespace) ,如果变为 null 或"" ,则返回""
    下面是示例(注意和 trimToEmpty() 的区别):
StringUtils.stripToNull(null) // ""
StringUtils.stripToNull("") // ""
StringUtils.stripToNull(" ") // ""
StringUtils.stripToNull("     \b \t \n \f \r    ") // "\b"
StringUtils.stripToNull("     \n\tss   \b") // "ss   \b"
StringUtils.stripToNull(" d   d dd     ") // "d   d dd"
StringUtils.stripToNull("dd     ") // "dd"
StringUtils.stripToNull("     dd       ") // "dd"

猜你喜欢

转载自blog.csdn.net/vayne_xiao/article/details/109784166