Java进阶基础知识
- 1、认识常用的数据类型
- 1.1 字节
- 1.2 数组创建:
- 1.3 对象数组的内存
- 1.4 字符串
- 2、 构造方法
- 3 、继承
- 4、单例模式(饿汉式单例模式)
- 4.1、懒汉式单例模式
- 5、类
- 5.1 嵌套类
- 5.2 内部类
- 5.3 静态嵌套类
- 5.4 局部类
- 5.5 抽象类
- 5.6 匿名类
- 6、多态
- 7、接口
- 8、Lambda
- 8.1 方法引用
- 9、枚举 enum
- 10、数字
- 10.1 包装类
- 10.2 自动装箱
- 10.3 自动拆箱
- 10.4 Math
- 10.4 Random
- 10.5 UUID
- 10.6 字符串转数字
- 10.7 数字转字符串
- 10.8 高精度计算
- 11、日期
- 11.1 SimpleDateFormat
- 11.2 Calendar
- 12、异常 Exception
- 12.1 异常处理
- 12.2 finally
- 12.3 throws
- 12.4 throw
- 12.5 自定义异常类
- 12.6 写一个断言类
- 13、正则表达式
- 13.1 单字符匹配
- 13.2 预定义字符
- 13.3 量词
- 13.4 Patter 、Matcher类
- 13.5 捕获组
- 捕获组 - 反向引用
- 13.6 边界匹配符
- 13.7常用模式
1、认识常用的数据类型
1.1 字节
- byte一个字节,int 4个字节 ,short 2个字节 ,double 8个字节 long 8个字节
- 字符串拼接:使用+
- 位运算符 >>(有符号右移) >>>(无符号右移最左位用0补齐)
- if(age)为错误的,必须为条件判断的布尔类型
- &、|、^ 位运算符 面向整数,也可以对Boolean使用(&& 、||有短路功能)
- 拓宽基本类型转化(自动转换)
- 窄化基本类型转换(强制)丢失精度 布尔类型不能和数字的基本类型进行转换
- 一元数字byte、short、char自动提升为int类型;执行一元运算符的时候(++ – ~ 左右移这些单目的运算符,还有数组的索引)称为一元数字提升
- 一元数字提升 byte、short、char自动提升为int类型 char c = ‘A’ +c->转为了整数类型
- 二元数字提升(多目运算) 拓宽 a + b,如果a是double\long\float,那么b也转为对应的类型,不是上边那三种的,那么都转为int
- 标识符:flag,由java字母,java数字组成,必须有java字母开头
- 变量名,方法名命名规则:小驼峰msFlagTrue
- 常量(全局)全大写下划线区分
1.2 数组创建:
int[] arr1 = new int[4];
int[] arr2 = {}; int arr3[] = {};
字符数组 char[] arr4; 字符串String 不相等;
- 数组属于引用数据类型,数组元素存储在堆空间中,arr相当于一个栈侦,局部变量存于栈内存当中;堆空间会自动进行初始化
- 数组长度arr.length
- 数组访问内存地址:随机访问 int arr[2] > 0x1010(arr的内存首地址)+ 2*4 int有4个字节
- 方法,可变参数 (必须是最后一个参数)
1.3 对象数组的内存
public static int sum(int... n1){ n1相当于一个数组的首地址
int result = 0;
System.out.println(n1.length);
for (int i : n1) {
result += i;
}
return result;
}
public PrintStream printf(String format, Object ... args) {
return format(format, args);
}
System.out.printf("my name is %s ,age is %d%n",name,age);
- 参数传递:基本类型是值传递,返回的是值 ,引用类型是地址值传递,返回的是地址值;
- 方法签名 方法名+参数类型 组成 sum(int,long,double)
- 方法重载:方法名相同,方法签名不同也就是参数个数、参数类型不同
- 栈侦(栈内存) 随着方法的调用而创建,随着方法结束而销毁,存储了方法的局部变量信息
- 递归调用:如果一直递归调用将一直消耗栈内存空间,内存空间溢出
- 对象的局部变量,局部方法,都在栈空间中开辟栈内存地址,成员变量在堆空间(没有被引用时会自动被回收)
- 引用变量是地址值传递。 方法存储
- java程序的内存划分:
- PC寄存器(存储正在执行的字节码指令地址)
- 虚拟机栈、堆、
- 方法区(字段和方法信息,不包含具体的值,构造方法和普通方法 )
- 本地方法栈,用来调用native,c语言编写的方法
1.4 字符串
- java.lang.String
- 底层使用char[]数组存储字符数据。java9开始使用byte[]
- String s = “13” 相当于String s = new String(“13”) 所以13是放在堆空间中的
- String s = “555”
- s = “666”
- 栈空间的指针变量的指向发生了改变
final关键字 - 修饰的类为最终类,不能被继承
- 修饰的方法不能被重写
- 修饰变量,只能进行一次赋值;
- 修饰成员变量:在声明中赋值,构造方法,静态代码块中,不能在创建完实例对象载进行赋值;
final static int age = 10;
常量
写法public static final int AGE = 10;
字符串常量池(SCP)
- 在堆空间中,当遇到字符串字面量时会先去SCP查看
- String s = “123”//字符串字面量,String s2 = new String(s)字符串字面量相同,就返回常量池中的对象value值,如果SCP中存在与字面量内容一样的字符串对象时,就返回该对象,如果没有就创建该对象,并加入SCP中,返回
intern方法
String s1 = new String("123");
String s2 = new String("123");
String s3 = s1.intern(); 有存在与s1字符串字面量一样的字符串对象时,就返回C;否则s1加入到SCP中,返回s1
String s4 = s2.intern();
String s5 = "123";
字符串常用方法
- jdk开发文档
StringBuilder
- 在字符串需要进行大量改动的时候(拼接、替换),如果使用String会非常消耗内存,降低程序性能,因为String在进行拼接、替换的时候都是直接在堆空间new一个新的字符串对象
StringBuilder sb = new StringBuilder();
sb.append("123").append("263");
- 常用方法还有:append、insert、delete、replace、reverse等
- StringBuilder并不是String的子类或者父类,和String实现了同一个接口CharSequence()
- StringBuilder的append原理,跟动态数组原理差不多。动态数组的扩容,默认容量是16,扩容后的16*2 + 2
2、 构造方法
- 方法名和类名一样,没有返回值,可以重载;
- this指向当前对象引用(访问当前类中定义的成员变量,调用当前类中定义的方法也包括构造方法(构造方法中调用另外一个构造方法 this(参数))) this为隐藏方法参数
- 默认构造方法:public 类名(){} 作用是初始化我们的成员变量
- package包的命名规则:公司域名倒写开头比如:com.baidu.*
- 如何使用一个类:通过import包名.类名 方式
3 、继承
- extend 父类引用指向子类对象
- 类继承可以继承多个
- Object类 顶级父类
- 同名的成员变量
- 方法的重写:子类的方法签名与父类的一样。 子类的方法权限必须大于父类的方法权限;子类的返回值类型必须小于父类的返回值类型
4、单例模式(饿汉式单例模式)
- 一个类只能创建一个实例对象
- 构造方法私有化
- 公共的静态的方法,返回唯一的一个实例
private static Dog instance = new Dog();
private Dog(){}
public static Dog getInstance(){
return instance;
}
4.1、懒汉式单例模式
- (并非线程安全)
private static Dog instance = null;
private Dog(){}
public static Dog getInstance(){
if(instance == null){
instance = new Dog();
}
return instance;
}
- 子类对象的内存中,依然包含父类中定义的private成员变量
- 虽然子类对象不能直接访问,但是能通过调用父类的Get/Set方法就可以知道是存在的。
5、类
5.1 嵌套类
public class Dog{
静态嵌套类
static class Cat{}
class B{}内部类
}
5.2 内部类
- 没有被static关键字修饰的嵌套类
- 跟实例变量、实例方法一样,内部类与外部类的实例相关联
- 必须先创建外部类实例,再用外部类实例创建内部类实例
Dog dog = new Dog();
Dog.Cat cat = dog.new Cat();//创建内部类实例对象
- 内部类不能定义除了常量之外的任何static成员
- 堆内存划分情况:会创建一个内部类的堆内存存放成员变量,还有一个指针指向外部类;
- 内部类可以访问外部类所有的成员,即使是private
- 外部类也可以直接访问内部类的成员,即使是private
5.3 静态嵌套类
public class Dog{
private static int age;
静态嵌套类 静态内部类 在外部类被装载的时候,静态内部类并不会立刻装载
static class Cat{
Dog.age; 可以直接通过类名进行访问外部类中的成员(类变量、类方法),实例变量,实例方法(需要创建外部类的实例对象才能进行访问)
}
class B{}//内部类
}
//创建静态嵌套类的实例
Dog.Cat cat = new Dog.Cat();
什么时候用到嵌套类
- 一个类只用到某一个类的内部的时候,可以把这个类嵌套
- 封装新更好,程序包更简化,可读性
- 需要经常访问一个类中的非公共成员,可以在这个类中嵌套一个类
- 什么时候用内部类,或者静态嵌套类
- 需要经常访问非公共的实例成员,设计成内部类,否则设计成静态嵌套类
- 如果必须先有C实例,才能创建A实例,可以将A设计成为C的内部类
- 内部类、静态嵌套类都可以定义实例变量、实例方法。
5.4 局部类
- 定义在代码块中的类(可以定义在方法中、for循环中,if语句中)
- 局部类中不能定义除了常量之外的任何static成员
- 局部类中只能访问final修饰的局部变量,或者有效final的局部变量
- 有效final:从java8开始,局部变量没有被第二次赋值,就认定为有效的final
- 可以直接访问外部类的所有的成员,即使是private修饰的
5.5 抽象类
- 抽象方法
protected abstract void test();只能被声明,不能被实现,不能是private权限
- 只能是实例方法
- 只能定义在抽象类、接口中
- 抽象类不能直接被实例化,但是可以定义构造方法、静态变量、实例变量、初始化块、 嵌套类
- 常见的使用场景:抽取子类的公共实现到抽象父类中,要求子类必须要单独实现定义的抽象方法;
- 普通类与抽象类有什么区别:普通类的子类不一定要全部实现父类中的所有方法,但是抽象类的子类必须要实现所有的抽象方法。
5.6 匿名类
- 当接口、抽象类的实现类,在整个项目中只用过一次,可以考虑使用匿名类(通俗点将就是直接new一个接口或者抽象类)
public interface Runnable{
void run();
}
public static void main(String[] args){
new Runnable(){
@overide
void run{...}
};
//或者
new Runnable(){
}.run();
}
- 匿名类不能定义除编译时常量意外的任何static成员
- 匿名类只有在实例相关的代码块中使用,才能直接访问外部类中的实例成员;也就是如果匿名类放在静态代码块或者静态方法中,不能够直接访问外部的实例成员。
匿名类的用途:
- 代码块传递
- 定义一个工具类:计算执行一段代码所用的时间
- 过滤器
- 回调
6、多态
- 多态体现:
- 父类类型指向子类对象
- 调用子类重写的方法
- 多态中类变量、成员变量的访问细节
public class Animal{
public static void run(){}
}
public class Dog extends Animal{
public static void run(){}
}
Animal dog = new Dog();
dog.run();调用的是Animal中的run方法,因为静态方法只看类型
instanceof
- 判断某个类型是否属于某种类型
public class Animal{}
public class Dog extends Animal{
Object dog = new Dog();
if(dog instanceof Dog) //true
}
7、接口
- 接口中可以定义抽象方法、常量(static final)可以省略、嵌套类型、java8开始可以定义:静态方法(类方法)、默认方法
- 均是隐式public
- 默认就是抽象方法可以省略abstract关键字
- 不能直接实例化
- 接口一般放行为规范
- 当父类、接口中的方法签名一样时,那么返回值类型也必须一样。
接口
public interface Behavior{
void eat(String name);
}
父类
public class Animal{
public void eat(String name);
}
子类
public Dog extends Animal implements Behavior{
@override
void eat(){}
}
- 接口之间也可以继承其他接口,extends
##接口升级问题(增加新的功能) - 使用默认方法
public interface Dog{
void eat();
default void sleep(){
.... 使用默认方法实现
}
}
- 接口中定义的静态方法只能通过接口名调用、不能被继承
public interface Dog{
static void eat(){}
}
排序
- 默认升序
Arrays.sort(array);
- 降序:
Arrays.sort(array,new Comparator<T>{
@overide
public T compare(T o1, T o2){
return o1 - o2; //升序
return o2 - o1; //降序
}
});
8、Lambda
- 函数式接口:只包含一个抽象方法的接口;
@FunctionalInterface //函数式接口注解
public class Dog{
void eat();
}
//标准写法
(参数列表) -> {
return 返回值
};
//参数列表可以省略参数类型
//只有一个参数的时候,小括号可以省略
//只有一条语句的时候,可以省略大括号、return、分号
//(参数列表) -> 返回值
- 匿名类实现的是函数式接口时,可以使用Lambda优化
- Lambda表达式没有引入新的作用域
public class OuterClass{
private int age = 1;
public class InnerClass{
private int age = 2;
void inner(){
//Lambda
int v = 10;//错误写法
Testtable t1 = v ->{
@Override
publice void test(int v){
v;
age;
this.age;//代表OuterClass对象
InnerClass.this.age;
OuterClass.this.age;
};
//匿名内部类
Testtable t2 = new Testable(){
@Override
publice void test(int v){
v;
age;
this.age;//代表匿名内部类的当前对象
InnerClass.this.age;
OuterClass.this.age;
};
}
}
}
}
8.1 方法引用
- 如果Lambda中的内容仅仅是调用某个方法,可以使用方法引用来简化,引用特定对象的实例方法
Public class Person{
public void setAge(int age){
System.out.println(age);
}
}
static void execute(new Persion()::setAge,20){} //new Persion()特定对象
9、枚举 enum
- 如果一个变量的取值只可能是固定的几个值,可以考虑使用枚举
public enum Enum {
Spring,Summer,Fall,Winter;
}
- 枚举由一组预定义的常量构成
- 本质就是一个类,所有枚举类型最终都隐式继承自java.lang.Enum
- 在定义完常量之后、枚举也可以定义成员变量、方法等
- 枚举构造方法权限必须是无修饰符或者private
- java会主动调用构造方法去初始化每一个常量,外界不能主动构造方法。
public static void main(String[] args) {
test(Enum.Spring);
}
public static void test(Enum season){
switch (season){
case Spring:
System.out.println("春天");
break;
case Summer:
System.out.println("夏天");
break;
case Fall:
System.out.println("秋天");
break;
case Winter:
System.out.println("冬天");
break;
}
}
}
10、数字
- 基本类型的缺陷(相比引用类型)
- 无法表达null值
- 当方法参数是引用类型时,基本类型无法传递
- 不能利用面向对象的方式去操作基本类型
- 解决方案:可以将基本类型包装成引用类型。
public class IntObject{
private int value;//当为public时,不用提供get/set方法
public IntObject(int value){
this.value = value;
}
}
10.1 包装类
- java中已经内置了基本类型的包装类(java.lang包中)
- 数字类型的包装类最终继承java.lang.Number
10.2 自动装箱
- java编译器会自动调用xxxvalueOf方法,将基本类型转换为包装类
Integer i = 10;
相当于
int i = 10;
Integer.valueOf(i);
10.3 自动拆箱
- java编译器会自动调用xxxvalue方法,将包装类转换为包基本类型
Integer i = 10;
int i2 = i;
相当于
int i2 = Integer.value(i);
- 包装类的判等,推荐使用equals方法,不推荐使用== !=
- Integer类型里边有缓存范围[-128,127],优先去缓存里边取出Integer对象
Integer i1 = 88;
Integer i2 = 88;
Integer i3 = 888;
Integer i4 = 888;
if(i1 == i2)//true
if(i2 == i3)//false
if(i1.equals(i2))//true
if(i3.equals(i4))//true
- 基本类型数组和包装类数组之间是不可以自动装箱,拆箱的。只能通过遍历数组调用ValueOf方法
public static void main(String[] args) {
int[] arr1 = {1,2,45};
Integer[] arr2 = new Integer[arr1.length];
for(int i = 0; i < arr1.length; i++){
arr2[i] = Integer.valueOf(arr1[i]);
}
System.out.println(arr2);
}
10.4 Math
- Maht.random[0.0,1.0)
10.4 Random
Random r = new Random();
Random random = new Random();
int i = random.nextInt(100);//[0,99)
System.out.println(i);
public static void main(String[] args) {
//随机输出4位的大写字母验证码
//A-Z-->A+[0,25]
for (int i = 0; i <4; i++){
char c = (char) ('A' + new Random().nextInt(26));
System.out.print(c);
}
}
10.5 UUID
- 通用唯一标识符
System.out.println(UUID.randomUUID());生成一个128bit的随机UUID码。
数字格式化
long n = 45623;
System.out.format("my age is %d%n",n);
String.format()
10.6 字符串转数字
- 调用包装类的valueOf\parsexxx(parseInt)
String s = "135";
System.out.println(Integer.valueOf(s));转的为引用类型
System.out.println(Integer.parseInt(s));转的为基本类型
10.7 数字转字符串
- Integer.toString();
10.8 高精度计算
- float\double存储只是小数的近拟值
- 使用Java.math.BigDecimal来进行高精度的计算
- 推荐使用字符串初始化BigDecimal
- BigDecimal b = new BigDecimal(“0.7”);
- a.add(b) -->a + b
- a.substract(b) -->a -b
- a.multiply(b) -->a * b
- a.divide(b) -->a/b
- a.setScale(3) 保留三位小数
11、日期
- java.util.Date,日期类
Date date1 = new Date();
date1为当前中国标准时间
Date data2 = new Date(123);
data2 123+1970-01-01 00:00:02
Data data3 = new Date(System.currentTimeMills);返回的也是当前时间
11.1 SimpleDateFormat
SimpleDateFormat fmt = new SimpleDateFormat("yyyy年MM月dd日 HH:mm:ss");(yyyy-MM-dd)
fmt.format(Date);日期转为字符串
fmt.parse(String);字符串转为日期
11.2 Calendar
- java.util.Calendar,日历类,是一个抽象类,不可以直接实例化
Calendar c = Calendar.getInstance();
修改日历
c.set(2018,09,12);
c.add(Calendar.MONTH,2);在这个月的基础上在加两个月
12、异常 Exception
- 所有异常都继承自java.lang.Throwable(异常的基类)
- RuntimeException–>Exception–>Throwable; Error–>Throwable ;除了Error、RuntimeException以外的异常称为检查型异常,不处理的话,编译不能通过
StringBuilder s = null;
s.append("123");
//java.lang.NullPointerException
12.1 异常处理
- 1、try-catch
- 2、throws
try{
StringBuilder s = null;
s.append("123");
Class<?> cls = Class.forName("com.lanren.Dog");
Dog dog = (Dog) cls.newInstance();
dog.test();
}catch (Exception e){
System.out.println("抛出了异常");
}
System.out.println("代码正常执行!");
- 父类型的异常必须写在子类型的后面
Throwable中的常用方法:
StringBuilder s = null;
s.append("123");
catch (Exception e){
System.out.println(e);
//java.lang.NullPointerException
catch (Exception e){
System.out.println(e.getMessage());
} //null
catch (Exception e){
e.printStackTrace();
}// java.lang.NullPointerException
at com.lanren.Main.main(Main.java:7)
- 单个catch可以捕获多个异常;catch (NullPointerException | IllegalAccessException e)
12.2 finally
- try或catch正常执行完毕之后,一定会执行finally中的代码
- finally代码块中经常放一些关闭、释放资源的代码。
- try\catch,使用了return\break\continue等提前退出的 关键字,finally中的代码仍会在这些关键字之前执行
12.3 throws
- 将异常抛给上层方法,一层层往上抛,当将异常抛给JVM的时候,程序就会终止运行。
- 如果父类的方法没有throws异常,子类的重写父类的方法也不能有throws异常
- 当父类的方法有throws异常,子类的重写方法可以没有throws,也可以跟父类一样的throws
12.4 throw
- 使用throw可以抛出一个新建的异常
- throw new Exception("");可以抛出检查和非检查型异常
- 也可以使用return
public void setAge(int age) throws Exception {
if (age <= 0){
throw new Exception("输入的年龄不合法!");
}else{
this.age = age;
}
12.5 自定义异常类
- 一般有两种,一种继承Exception,检查型异常;一种继承RuntimeExceoption,
12.6 写一个断言类
public class Assert{
public static void test(boolean b){
if (b){
return;
}
try {
throw new IllegalAccessException("条件不成立");
} catch (IllegalAccessException e) {
e.printStackTrace();
}
}
Assert.test(sum(10,20) == 30);
Assert.test(sub(30,20) == 10);
}
public static int sum(int a, int b){
return a - b;
}
public static int sub(int a, int b){
return a - b;
}
13、正则表达式
- 语法 作用搜索一个字符串
String regex = "正则表达式";
String s = "1354";
s.matches(regex);//要求完全匹配
13.1 单字符匹配
13.2 预定义字符
- 以一个反斜杠开头的字符会被当做转义字符处理,因此我们的预定义字符需以两个反斜杠开头\
- \* \& 两个反斜杠后接特殊字符也可以表示单个的特殊的字符
13.3 量词
13.4 Patter 、Matcher类
- string的matches方法底层用到了Patter 、Matcher这两个类
//通过一个正则表达式搜索一个符合条件的子列
String regex = "\\d{3}";
String s = "_123_456";
findAll(regex,s); //返回结果:123,[1,4] 456,[5,8]
//通过一个正则表达式搜索一个符合条件的子列
String regex = "a?";
String s = "abbcaa";
findAll(regex,s);
//返回结果
a,[0,1]
,[1,1]
,[2,2]
,[3,3]
a,[4,5]
a,[5,6]
,[6,6]
public static void findAll(String regex,String input){
if (regex == null || input ==null){
return;
}
Pattern p = Pattern.compile(regex);
Matcher m = p.matcher(input);
boolean flag = false;
while (m.find()){
flag =true;
System.out.format("%s,[%d,%d]%n",m.group(),m.start(),m.end());
}
if(!flag){
System.out.println("bjfhe");
}
}
- Matcher 贪婪、勉强、独占的区别
- 贪婪:先对整个input字符串进行匹配,若匹配失败,则吐出最后一个字符,然后再次尝试,重复此操作,直到匹配成功
String regex = ".*foo";
String s = "afooadfgefgfooa";
findAll(regex,s);
//返回结果afooadfgefgfoo,[0,14]
- 勉强:先吞掉input字符串的第一个字符进行匹配,若匹配失败再吞下下一个字符,然后再次尝试,重复此操作,直到匹配成功
String regex = ".*?foo";
String s = "afooadfgefgfooa";
findAll(regex,s);
返回值:afoo,[0,4]
adfgefgfoo,[4,14]
- 独占:吞掉整个inpu进行唯一一次的匹配
String regex = ".*+foo";
String s = "afooadfgefgfooa";
findAll(regex,s);
//返回值no match
13.5 捕获组
regex = “(abc){3}”;
只有abcabcabc才匹配。因为abc是一组;
捕获组 - 反向引用
- 可以使用反斜杠(\)+组编号(从1开始)来引用组的内容
String regex = "(\\d\\d)\\1";
String input = "1212";
System.out.println(input.matches(regex));//true
13.6 边界匹配符
终止符:/r回车符 /n换行符
13.7常用模式
常用的正则表达式
- (https://c.runoob.com/front-end/854)
- String里有三个方法支持正则表达式
String s1 = "Therow row we are longling for is row 8.";
String s2 = s1.replace("row", "line");
String s3 = s1.replaceAll("\\brow\\b", "line");
System.out.println(s2);
System.out.println(s3);
//返回结果
Theline line we are longling for is line 8.
Therow line we are longling for is line 8.
String s1 = "ab12365geg78ai98kj";
String[] split = s1.split("\\d+");
for (String s : split) {
System.out.println(s);
}
//返回结果
ab
geg
ai
kj
String s1 = "aa11ab56bb66";
String regex = "(\\w)\\1(\\d)\\2";
Pattern p = Pattern.compile(regex);
Matcher m = p.matcher(s1);
while (m.find()){
System.out.println(m.group(1)+"_"+m.group(2));
}
//返回结果
a_1
b_6
//捕获组应用:捕获字符串以某种格式找出来。