重写与重载区别
重写:是指子类继续父类后,不想用父类方法就自己重新写了一个方法,如何判定“重新”呢,即父类与子类的方法的参数个数,参数类型完全一致时视为同一个方法。
重载:一个类中的多个方法的名称相同,参数个数或者参数类型不同,则称为重载方法,简单说就是虽然在同一个类中,两个方法很相似,但终究不是同一个,因为参数个数或者参数类型不同,调用时只能明确调用某一个。
封装,继承,多态
-
封装:将对象的具体信息隐藏起来,不对外暴露,需要访问时通过提供的方法来操作,简单说:该隐藏隐藏,该暴露暴露。
-
继承:子类继承父类后,共性的东西子类不用写,全都默认拥有。 通过继承能够复用父类代码,提高开发的效率。
-
多态:同一个对象 调用同一个方法,呈现出不同的表现。
发生的前提: 继承关系 且 重写方法
父类引用 指向 子类对象 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条 就是:10,5
或者
一个指定的页数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 都是1为1 都是0为0 否则为0
A|B 0011 1101 有一个1为1 否则为0
A^B 0011 0001 相同0否则1
~B 1111 0010 每一位取反
左移 2<<3 16 左移3为就相当于乘以2的3次方
0000 0010 这是2
0001 0000 这是16 效率极其高
右移 16>>3 2 右移3为就相当于除以2的3次方
条件运算符 ?: 也叫三目运算符 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; static和final都是修饰符,不分前后
对象初始化顺序
但如果继续创建一个同类对象就不在执行 “静态变量 和 静态初始化块”,只执行后两者
父类静态变量、父类静态初始化块、
子类静态变量、子类静态初始块、
父类变量、父类初始化块、父类构造函数、
子类变量、子类初始化块、子类构造函数。
构造器
默认有无参构造;
如果存在有参构造,就必须显示定义无参
对象引用 栈堆(谐音:战队)
栈内存保存引用地址,堆内存保存对象的内容,即属性和属性值
引用传递
产生垃圾
异常分类
- 检查异常:(必须抛出)
所有继承自Exception类的异常,称为检查异常 Checked Exception
该类异常是可预期的,很有可能会发生
编译器要求必须显式处理该异常,即编写代码时就强制要处理 - 运行时异常(可以不抛出,运行时才有可能发生)
所有继承自RuntimeException类的异常,称为运行时异常
该类异常并是否发生不可预测
如果代码没有逻辑性错误,是不会出现运行时异常
编译器不要求必须处理该异常,即编写代码时可以不处理
异常的产生和处理
- 产生
每种异常都是使用一个Java类来表示
异常的产生:- 当程序发生异常时,会自动生成一个对应异常类的对象,然后将该异常对象提交给JRE,这个过程称为抛出异常 throw
- 当JRE接收到异常对象时,会寻找能处理此异常的代码并把当前异常对象交给其处理,这个过程称为捕获异常 catch
- 如果JRE找不到可以捕获异常的代码,则系统终止,程序将退出所以需要对异常进行处理,否则程序将立即终止,无法继续执行。
- 处理
异常处理的两种方式:两种方式效果相同,只是手动抛出的信息更相信,建议手动抛出
- 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");
}
}
异常的定位和解决
查找异常的出现的位置并解决:
- 首先查看有没有Caused by,如果有则从Caused by开始找,如果没有则从头开始找
- 然后找到第一行自己写的代码,问题就在这里
- 最后根据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 转换数字调整精度
- 以“0”补位时:
如果数字少了,就会补“0”,小数和整数都会补;
如果数字多了,就切掉,但只切小数的末尾,整数不能切;
同时被切掉的小数位会进行四舍五入处理。
- 以“#”补位时:
如果数字少了,则不处理,不会补“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"