JAVA基础语法
注释
单行注释//,多行注释/**/,注释快捷键ctrl+/(单行注释),ctrl+shift+/(多行注释)
变量
字面量
对数据类型写法的规范:
数据 | 写法 |
---|---|
整数 | 123 |
小数 | 12.13 |
字符 | ‘A’ |
字符串 | “Hello world” |
布尔值 | true false |
空值 | null |
变量
变量定义:数据类型 变量名 = 数据;
变量赋值:变量名 = 数据;
注意变量的作用域问题,局部变量,成员变量。
数据类型
数据类型
整形(byte,short,int,long),浮点型(float,double),字符型(char),布尔型(boolean),字符串型(string)
类型转换
自动(表达式)类型转换:数据范围大的类型可以之间由范围小的类型定义得到
强制类型转换:(数据类型)变量
运算符
算数运算符
+,-,*,/,%(取余)
自增自减运算符
++,–
a++为先用再加,++a为先加再用
赋值运算符
(+,-,*,/)=
关系运算符
<= >= = != > <
逻辑运算符
&,!,|,^(异或),&&(短路或),||(短路或)
双与或当左边条件判断失败时,就已经不成立了,故使用效率更高
三元运算符
条件?a1:a2. 条件为true返回a1,否则返回a2
流程控制
分支结构
if分支
//if结构
if(条件){
xxx
}
//else结构
if(){
...
}
else{
}
//else if结构
if(){
}
else if(){
...
}
else{
}
switch分支
switch(表达式){
case 值1:
...
break;
case 值2:
...
break;
default:
...
}
switc具有穿透性,若不使用break,将会继续向下输出。
循环结构
for循环
for(int i=0;i<3;i++){
system.out.println("hello world")
}
while循环
while(i<5)
{
i++;
}
do-while循环
do{
i++;
}while(i<4);
//和while的区别就是先做后判断,基本用不上。。。
break,continue
区别:break直接结束当层循环,continue只结束此次循环,不影响下一次循环的执行
数组
静态初始化数组
初始化数组
//完整格式
数据类型[] 数组名 = new 数据类型[]{元素};
//简化格式
数据类型[] 数组名 = {元素};
//数据类型[] 数组名 等同于 数据类型 数组名[]
访问数组
数据名[索引];
数据的长度
arr.length;
动态初始化数组
动态初始化需要先确定数组的存储长度
数据类型[] 数组名 = new 数据类型[长度]
方法
就是函数
方法定义
修饰符 返回值类型 方法名(形参列表){
方法体代码;
return 返回值;
}
//方法调用
方法名(形参列表);
修饰符暂时使用public static(公用静态),以后再做描述
方法重载
在同一个类中,有多个方法名称相同,但形参列表不同,调用时不会发生冲突,称为方法重载
public class heya{
public static test(){
system.out.println('n');
}
public static test(int n){
system.out.println((char)n);
}
}
面向对象编程
类与对象
OOP思想,三大特征为:封装,继承,多态。在java中,同一个包(packge)下的类可以互相使用
//1.创建类
public class Student{
string name;
double chinese;
double math;
public void printTotalScore(){
System.out.println("总成绩为:"+(chinese+math));
}
public void printAverageScore(){
System.out.println("平均分为:"+(chinese+math)/2);
}
}
//2.创建对象,封装数据
Student s1 = new Student();
s1.chinese = 100;
注意,一个代码文件可以用多个类,但只能有一个类用public修饰,并且用public修饰的类名必须和文件名相同,也只有这个类可以被其他包下的程序调用
this
为一个变量,可以用来在类的创建中替代对象,主要应用:
//解决变量名访问冲突
public class student{
double score;
public void printPass(double score)//这个score是形式参数,是对象进行方法调用时,从外部传入的数据
{
if(this.score>score)//这个this.score是对象对类的实例调用,即类中定义的score
System.out.println("win");
}
}
//-----------------------------------------------实例化如下------------------------------------------------------
student s1 = new student();
s1.score=305;
s1.printPass(254);//结果为win
构造器
又称构造方法(其实就是构造函数哈),创造对象的时候实际上就是调用构造函数来生成对象,重载有参构造函数可以简化后续对对象的赋值过程
Public class Student{
String name;
double score;
public Student(){
"无参构造器";
}
public Student(String name,double score){
"有参构造器";
this.name = name;
this.score = score;
}
}
Student s1 = new Student();//调用无参构造器
Student s2 = new Student("张元英",20);//调用有参构造器
注意:类在设计时,若为声明构造器,java会为类自动生成一个无参构造器。但是如果已经声明了有参构造器,就必须手动添加一个无参构造器。
封装
合理运用public和private对成员进行封装,只对外暴露有限接口
public class Student{
private double score;//对数据进行隐藏,防止篡改
public void set score(double score){
if(score>=0&&score<=100)
this.score=score;
else
System.out.println("数据非法")
}//可以通过setscore方法来进行访问权限的控制
public double getscore(){
return score;
}//可以通过score来为外界提供读取接口
}
实体javabean
符合以下两个要求的类称为实体类:1.类中成员变量都需要私有,并对外提供get和set方法。2.类中必须要有一个公共的无参构造器,这样做是为了将实体类与其他类分开,实体类只用于负责数据存取
public class Student {
private String name;
private int age;
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
static
静态,用于修饰成员变量和成员方法。
1.static修饰成员变量
成员变量按照有无static修饰,分为类变量和成员变量
public class Student{
static String name;//类变量
int age;//实例变量
}
"类变量有static修饰,属于类,在计算机中只有一份,会被类的所有全部对象共享"
"实例变量属于对象,每个对象都会有自己独有的实例变量"
"类变量推荐使用 类名.类变量 的方式进行访问,而实例变量只能通过 对象.实例变量 的方式访问"
2.static修饰成员方法
同上,类方法属于类,实例方法属于对象
public class Student{
public static void getstudents(){};//类方法
public int getage(){};//实例方法
}
"同理,类方法推荐使用 类名.类方法 的方式进行访问,而实例方法只能通过 对象.实例方法 的方式访问"
3.类变量和类方法的应用场景
类成员主要用于对整个类做标记,如使用count来记录已经生成多少个对象,或者用id来标记这是编号第几的类
类方法主要用于制作工具类,类似于Algorithm,Random等等等等,他主要的作用是向外界提供数据处理的方法
4.注意事项
- 类方法可以访问类成员,但不能访问实例成员
- 实例方法可以访问类成员,也可以访问实例成员
- 类方法中不能使用this关键字
5.代码块
代码块属于类的5大成分之一(成员变量,构造器,方法,代码块,内部表)
//静态代码块
//格式:static{}
public class Student{
static{
...
}
}
//特点:类加载时自动执行,只会执行一次,用于完成类的初始化
//实例代码块
//格式:{}
public class Student{
{
...
}
}
//特点:每创建一个对象时就执行,在构造器之前执行,用于完成对实例变量的初始化
继承
基本语法
java提供了一个关键字extends,可以用这个关键字来让两个类建立起父子关系
public class B extends A{}
//其中A称为父类,基类或超类,B称为子类或派生类
继承的特点是:子类能够继承父类的非私有成员(变量,方法)
继承后对象创建:子类的对象由子类和父类共同完成
权限修饰符
主要有四种:public,private,protected或者不写
修饰符 | 本类 | 同一个包下的其他类 | 任意包下的子类 | 任意包下的任意类 |
---|---|---|---|---|
private | √ | |||
缺省 | √ | √ | ||
protected | √ | √ | √ | |
public | √ | √ | √ | √ |
单继承
java不支持多继承,即不支持一个子类有两个父类,但是java支持多层继承,子类还可以继续往下继承子类,同样可以做到多继承的效果。所有类都有一个最终的父类为Object类
方法重写
当子类觉得父类的某个方法具有局限性无法满足需求时,可以重写写一个方法名称,参数列表完全一样的方法来覆盖父类的这个方法,称为方法重写
注意:
-
可以使用Override来注明重写,并且可以检查重写是否正确
@Override public void print1(){}
-
子类重写父类时,访问权限必须大于或等于父类的权限
-
重写返回值类型必须小于等于被重写方法的返回值类型
-
私有方法和静态方法不能被重写
常见应用场景:通过重写Object类中的toString方法,来应对不同场景的转字符串操作等等
子类访问成员的特点
-
子类访问成员变量依照就近原则,父类和子类存在同名变量时优先访问子类变量
-
若需要访问父类的成员变量,则使用super关键字
public class z extends F{ String name = "son"; public void showName(){ System.out.println(name);//子类 System.out.println(super.name);//父类 } }
子类构造器特点
子类的所有构造器都会先调用父类的无参构造器,再调用自己。若要指定调用有参构造器,则必须手动添加super()方法。
class F{
public F(){}
}
class Z extends F{
public Z(){}
public Z(String name){}
}
//无论Z的有参还是无参构造器被调用,都会先调用F()
若要指定调用有参构造器,则必须手动添加super()方法。
class F{
public F(String name){}
}//此时只有有参构造器,没有默认生成的无参构造器
class Z extends F{
public Z(){
super("heya");//必须手动调用父类的有参构造器
}
public Z(String name){
super("heya");
}
}
子类构造器这样设计的应用场景:
//子类对象希望同时对对象直接初始化,而其中的某些变量是父类的私有变量
//Teacher t = new Teacher("小王",23,"舞蹈");
class People{
private String name;
private int age;
public People(){}
public People(String name, int age){
this.name=name;
this.age=age;
}
}
class Teacher extends People{
String skill;
public Teacher(){}
public Tcacher(String name,int age,String skill){
super(name,age);//通过super关键词来调用父类的有参构造器
this.skill=skill;
}
}
扩展:还可以通过this()的方法来调用兄弟构造器,来实现代码复用,减少代码量
class Student{
private String name;
private int age;
private String school;
public Student(){}
public Student(String name,int age){
this(name,age,"北京邮电大学");//用this()来减少不必要的代码量
}
public Student(String name,int age,String school){
this.name=name;
this.age=age;
this.school=school;
}
}
注意事项:this()不能和super()同时出现,并且this()必须在构造器的第一行。
多态
基本语法
多态是在继承和实现下产生的一种现象,表现为对象多态和行为多态
//对象多态
//Teacher和Student都是People的子类
public class Teacher extends People
public class Student extends People
People p1 = new Student();
People p2 = new Teacher();
//People类是Student和Teacher类的父类,范围更大,赋值合理,称为向下转型
//行为多态
//People中存在方法run
public void run(){sout("run!");}
//Student重写了方法run
@Ovrride
public void run(){sout("student runs fast!")}
//Teacher重写了方法run
@Ovrride
public void run(){sout("teacher runs hard")}
p1.run();//student runs fast!
p2.run();//teacher runs hard
//编译看左边,运行看右边
//多态的前提,要有继承/实现关系,存在父类引用子类,必须存在方法重写
//对于成员变量不存在多态,因为变量不存在重写的概念,调用成员变量时参照编译方的变量
//People.name=PEOPLE Student.name=STUDENT Teacher.name=TEACHER
sout(p1.name);//PEOPLE
sout(p2.name);//PEOPLE
为什么使用多态
在多态形式下,右侧对象为解耦合状态,便于系统的扩展和维护
//现有系统有如下语句
People p1 = new Student();
p1.run();
//当需要对Student类进行优化,变为Teacher类时,只需要对右侧对象进行更改
People p1 = new Teacher();
p1.run();
//假如不使用多态,即
Student s1 = new Student();
s1.run();
//在更新时则需要同时更新多个数据,操作麻烦
Teacher t1 = new Teacher();
t1.run();
定义方法时,使用父类类型的形参,可以接受所有子类对象,扩展性更强,更便利
//设计一个比赛方法
public void Competition(People p){
}
//此时无论是老师还是学生对象,都可以被接受
Teacher t = new Teacher();
Competition(t);
Student s = new Student();
Competition(s);
多态存在的问题
在多态下,无法调用子类的独有功能,此时,需要使用强制类型转换。
-
自动类型转换:父类 变量名 = new 子类()
-
强制类型转换:子类 变量名 = (子类)父类变量
People p1 = new Student(); Student s1 = (Student) p1;//强制转换People对象为Student对象,父类转子类 Teacher t1 = (Teacher) p1;//会报错ClassCastException,因为p1指向的是学生对象,会发生数据类型转换异常 //为避免异常报错,在进行强转前,通常会使用instanceof关键字,来进行流程优化 if(p1 instanceof Student) Student s1 = (Student) p1; else Teacher t1 = (Teacher) p1;final
final
final通常可以用来修饰类,方法,变量
//final修饰类,类不能被继承
final class A{}
//final修饰方法,方法不能被重写
public final void test(){}
//final修饰变量,变量只能被赋值一次
final int a;
public static void buy(final double money){}
public static final String SCHOOL = "北京邮电大学";//final修饰类变量(static final)称为常量,必须在赋值时就命名,且命名一般全大写,用下划线连接
//同时,final一般不用于去修饰实例变量,否则会导致每个不同对象的该变量都是同一个值,没有意义
抽象类
java中有个关键字abstract,由abstract修饰的类叫抽象类,由abstract修饰的方法称为抽象方法
public abstract class A{
public abstract void test();//抽象方法只能有签名,不能有方法体
}
注意事项:
- 抽象类中不一定有抽象方法,但有抽象方法的类一定是抽象类
- 抽象类可以拥有普通类拥有的成员(方法,成员变量,构造器)
- 抽象类不能创建对象,只用于作为父类让子类来继承
- 一个类若继承抽象类,则他必须继承其所有抽象方法或者自己也声明为抽象类
抽象类的好处:父类提供一个方法模板,由子类去重写实现,如对于Animal类,对每个动物都定义声音Sound方法,但每种动物的叫声不同,也无法将动物这个整体概念的叫声实例化,这时候就需把Animal类设计为抽象类,能更好的支持多态
接口
接口定义
java提供关键字interface,来定义出一个特殊的结构:接口。
public interface name{
//成员变量
//成员方法
}
//接口的成员变量默认为常量,成员方法默认为抽象方法
String SCHOOL_NAME;//省略了public static final
void test();//省略了public abstract
接口同样不能创建对象,接口用来被类实现(implements),接口实现的类称为实现类,一个类可以实现多个接口
public class A implements B,C,D{}
接口也和抽象类一样,其实现类必须重写所有接口的抽象方法,否则实现类被定义为抽象类
接口的优势
接口弥补了类单继承的不足,一个类可以实现多个接口。让程序可以面向对象编程,方便业务切换。(第二点类似于之前讲解的多态好处)
接口的新增方法(jdk8以上)
//1.默认方法,必须用default修饰,并且默认为public
default void test1(){}//可以带方法体,需要用实现类的对象来访问
//2.私有方法,需要用private修饰
private void test2(){}//由于接口不能创建对象,只能通过默认方法来调用
//3.静态方法,必须使用static修饰。默认为public
static void test3(){}//需要用接口名来调用
接口的其他注意事项
-
一个接口可以同时继承多个接口
interface A{} interface B{} interface C{} //若需要创建实现类 class test1 implements A,B,C{} //当继承接口过多时,会比较麻烦,此时,我们可以创建一个接口来继承这多个接口,再让类来实现这个接口,就可以解决 interface D extends A,B,C{} class test2 implements D{}
-
一个接口继承多个接口,若多个接口中存在方法签名冲突,此时不支持多继承(同理,用类实现多个接口时也会有这个问题)
//方法签名:方法名和形参列表 interface I{ String test(); } interface J{ void test(); } interface K extends I,J{}//会报错
-
一个类同时继承父类和实现了接口,若父类和接口中有同名默认方法,优先使用父类
class Fu{ public void run(){ sout("父类"); } } interface Itface{ default void run(){ sout("接口"); } } public class test extends Fu implements Itface{} test t = new test(); t.run();//"父类" //若要使用接口的方法,需要调用接口名.方法 t.Itface.run();
-
一个类实现多个接口,若多个接口中存在同名默认方法,不冲突,只需要重写方法即可
interface A{ default void run(){} } interface B{ default void run(){} } class C implements A,B{ @Override public void run(){ sout("重写"); } }
内部类
是类的组成成分之一,如果把一个类定义在另一个类的内部,这个类就是内部类。应用场景:当一个类内部需要一个完整的事务,并且他只在这个类中使用,没必要单独设计时,可以考虑定义内部类。内部类主要有四种内部类:
-
成员内部类(了解):类中的一个普通成员,类似于成员变量,成员方法
//构造 public class Outer{ public class Inner{ private String name; } } //调用 Outer.Inner in = new Outer().new Inner();
-
静态内部类(了解):同静态变量(类变量),他是属于外部类独有的类
//构造 public class Outer{ public static class Inner{} } //调用 Outer.Inner in = new Outer.Inner();//不需要先创建外部类对象,直接通过类名创建
-
局部内部类(了解):定义在代码块,构造器,方法体中的类
匿名内部类(重点):是一种特殊的局部内部类,不需要为这个类声明名字
//构造
//先有一个已定义的类或接口,此处以一个抽象类为例
abstract class Animal{
public abstract void cry(){}
}
//然后直接使用new结构进行创建内部类,在内部类中,还需要重写方法
new Animal(){
@Override
public void cry(){}
}
//生成匿名内部类的时候,实际上也new了一个以Animal继承或实现的对象,所以能够直接给对象赋值
Animal a = new Animal(){
@Override
public void cry(){}
}
//匿名内部类实际上的结构↓
class Test$1 extends Animal{
Test$1(){}
public void cry(){}
}
应用场景:
//1.作为参数传递给方法
//设计一个游泳方法,一个接口
public static start(Swimming s){
s.swim();
}
interface Swimming{
void swim();
}
//匿名内部类赋值接口创建新对象
Swimming dog = new Swimming(){
@Override
public void swim(){
sout("dog is suwimming")
}
}
start(dog);
//直接作为参数传递
start(new Swimming(){
@Override
public void swim(){
sout("dog is suwimming")
}
});
枚举
枚举是一种特殊的类,需用关键字enum声明
public enum A{
X,Y,Z;
}
//枚举类第一行都是罗列名称(常量),每个常量记住枚举类的一个对象
//枚举类构造器私有,不能对外创建对象
//枚举是最终类,不能被继承
//枚举类可以拥有其他类成员
//枚举取对象
A a = A.X;
//额外的枚举类API
A[] arr = A.values();//拿到A的所有对象
A a1 = A.valueOf("Z");
sout(a1.name());//"Z"
sout(a1.ordinal());//索引
//枚举遍历
for (A a : A.values()) {
System.out.println(a);
}
//含参枚举类
public enum Number {
THREE("3"),FOUR("4"),FIVE("5"),SIX("6"),SEVEN("7"),EIGHT("8"),
NINE("9"),TEN("10"),JACK("J"),QUEEN("Q"),KING("K"),ACE("A");
private final String value;
Number(String value) {
this.value = value;
}
public String getValue() {
return value;
}
//需额外提供getValue()函数来以便遍历
for(Number n : Number.values()) {
System.out.println(n.getValue());
}
另一种特殊的枚举是抽象枚举
public enum B{
X(){
@Override
public void go(){}
},Y(){
@Override
public void go(){}
}
public abstract void go();
}
//当枚举类中存在抽象方法时,该类成为抽象枚举类,此时每个枚举对象都需要重写这个抽象方法(匿名内部类)
应用场景
//枚举可以用来做信息标志和分类,可以严格限制传入的参数
public enum Constant{
BOY,GIRL;
}
public void check(Constant sex){
switch (sex){
case BOY:
break;
case GIRL:
break;
}
}
泛型
定义类,接口和方法时,同时声明了一个或多个类型变量<E>,称为泛型类(接口/方法),统称泛型
//约束可操作的数据类型E
//泛型类
//可以同时声明多个类型
public class ArrayList<E,T>{
//E可以作为形参声明变量
public boolean add(E e){}
//E也可以作为返回值表示返回的泛型
public E get(int index){}
}
//扩展:定义泛型类时,还可以运用继承等关系来限制接收类型必须是某个类本身或者他的子类
public class myclass<E extends Animal>{}
//泛型接口
public interface Data<E>{}
//泛型方法
public static <T> void test(T t){}
//通配符:在使用泛型时可以代表一切类型,此时默认不关心结构中的数据类型,也不能向其中添加数据(通常要对数据操作的集合也不会直接使用无界通配符)
public static void go(ArrayList<?> Cars){}
//使用泛型需要提前定义
public static <T> void go(ArrayList<T> Cars){}
//泛型上限:代表该泛型最高就是这个类,只能取他和他的子类
<? extends Car>
//泛型下限:代表该泛型最低就必须是这个类,只能取他和他的父类
<? super Car>
//注意:泛型不支持基本数据类型
Lambda表达式
用于简化函数式接口的匿名内部类。(函数式接口:只有一个抽象方法的接口,一般会在该接口上做@FuntionalInterface的标记)
//接口声明
@FunctionalInterface
interface Swimming{void swim();}
//匿名内部类
Swimming s = new Swimming(){
@Override
public void swim(){
sout("swim");
}
}
//用Lambda表达式简化匿名内部类
Swimming s = ()->{
sout("swim");
}
//方法体只有一行时也可以省略括号
Swimming s = ()->sout("swim");
//简化方法
Arrays.setAll(prices,new IntToDoubleFunction(){
@Override
public double applyAsDouble(int value){
return prices[value]*0.8;
}
});
//step.1 删除new到形参列表的内容
Arrays.setAll(prices,(int value){
return prices[value]*0.8;
}
});
//step.2 删除多余括号并补上箭头
Arrays.setAll(prices,(int value)->{
return prices[value]*0.8;
}
);
//其他省略规则(看情况使用,会使代码可读性下降)
//1.参数类型可以省略
Arrays.setAll(prices,(value)->{
return prices[value]*0.8;
}
);
//2.只有一个参数时,括号也可以省略
Arrays.setAll(prices,value->{
return prices[value]*0.8;
}
);
//3.只有一条语句时,大括号也可以省略,同时省略分号,如果此时该条语句是return语句,还要省略return
Arrays.setAll(prices,value->prices[value]*0.8);
方法引用
静态方法引用
当某个Lambda表达式里只调用了一个静态方法,并且前后参数形式一致,就可以用静态方法引用来进行简化
//形式:类名::静态方法
public class CompareByData{
public static int compareByAge(Student o1,Student o2)
return o1.getAge()-o2.getAge();
}
//lambda表达式
Arrays.sort(students,(o1,o2)->CompareByData.compareByAge(o1,o2));
//静态方法引用
Arrays.sort(students,CompareByData::compareByAge);
实例方法引用
当某个Lambda表达式里只调用了一个实例方法,并且前后参数形式一致,就可以用实例方法引用来进行简化
//形式:对象名::实例方法
public class CompareByData{
public int compareByAgeDesc(Student o1,Student o2)
return o2.getAge()-o1.getAge();
}
CompareByDate desCom = new CompareByDate();
//lambda表达式
Arrays.sort(students,(o1,o2)->desCom.compareByAgeDesc(o1,o2));
//实例方法引用
Arrays.sort(students,desCom::compareByAgeDesc);
特定类型方法引用
如果某个Lambda表达式里只调用一个实例方法,并且前面参数列表的中的第一个参数是作为方法的主调,后面的所有参数都是作为该实例方法的入参,则可以使用特定类型方法引用
//形式:类型::方法
//原码
Arrays.sort(names,new Comparator<String>(){
@Override
public int compare(String o1,String o2){
return o1.compareToIgnoreCase(o2);
}
});
//lambda表达式
Arrays.sort(names,(o1,o2)->o1.compareToIgnoreCase(o2));
//其中o1.com...代表o1是方法的主调,并且o2是入参,可以使用特定类型引用,由于o1o2都是String类型
//特定类型方法引用
Arrays.sort(names,String::compareToIgnoreCase);
构造器引用
如果某个lambda表达式里只是在创建对象,并且前后参数情况一致,就可以使用构造器引用
//形式:类名::new
interface CreateCar{
Car create(String name,double price);
}
CreateCar cc = new CreateCar(){
@Override
public Car create(String name,double price){
return new Car(name,price);
}
};
//Lambda表达式
CreateCar cc = (name,price)->new Car(name,price);
//构造器引用
CreateCar cc = Car::new;
正则表达式
用于限制字符串的输入格式,String类提供了matches方法用于使用正则表达式regex匹配字符串
//1.书写规则
//字符类(只能匹配单个字符)
[abc]:只能是a,b,c
[^abc]:除了a,b,c以外的字符
[a-zA-Z]:a到z,A到Z间的字符
//预定义字符(只能匹配单个字符),由于转义字符的存在,在java编写中应该使用\\来保证'\'为字符
.:通配符
\d:数字
\D:非数字
\s:空格
\S:非空格
\w:字母下划线或数字
\W:非w
//数量词,与匹配词结合使用
?:0次或者1次
*:0次或者多次
+:1次或者多次
{n}:正好是n次
{n,}:>=n次
{n,m}:>=n次且<=m次
//其他字符
(?i)a:忽略a字符的大小写
&&:和
|:或
():分组
//2.匹配字符串
public boolean matches(String regex);//返回一个布尔值表示是否匹配
//3.查找字符串
//定义爬取规则
String regex = "00?";
//封装Pattern对象
Pattern pattern = Pattern.compile(regex);
//获取匹配器
Matcher matcher = pattern.matcher(data);//data是查找的数据集
//爬取信息
while(matcher.find()){
String rs=matcher.group();
sout(rs);
}
//4.搜索替换
//按照匹配内容替换
public String replaceAll(String regex,String newstr);
//按照正则表达式匹配内容分割字符串,返回字符串数组
public String[] split(String regex);
异常
java中的异常有共同父类Throwable,其下主要有两大种类:
- Error:属于系统级错误
- Exception:用于提供给程序员的类,运行时异常(RuntimeException)在编译阶段不会报错,运行时出现错误会封装抛出。编译时异常(Exception)会在编译阶段就将错误抛出。
自定义异常
自定义RuntimeException
//1.定义异常类继承RuntimeException
public class AgeIllegalRuntimeException extends RuntimeException{
}
//2.重写构造器
public AgeIllegalRuntimeException(){}
public AgeIllegalRuntimeException(String message){
super(message);
}
//3.在函数中定义异常点抛出异常
if(xxx)xxx;
else{
throw new AgeIllegalRuntimeException("this age is out of bounds");
}
自定义Exception
//1.定义异常类继承Exception
public class AgeIllegalException extends Exception{
}
//2.重写构造器
public AgeIllegalException(){}
public AgeIllegalException(String message){
super(message);
}
//3.在函数中定义异常点抛出异常,并且需要在方法签名中使用throws来抛出方法内部异常
public static void saveAge2(int age) throws AgeIllegalException{
if(xxx)xxx;
else{
throw new AgeIllegalRuntimeException("this age is out of bounds");
}
}
异常处理
通常有两种方法,一种是记录异常并响应合适的信息给用户,另一种是捕获异常并尝试修复。但都会一层层向上抛,返回给调用者集中处理
1.响应信息
public static void main(){
//4.通过try,catch捕获异常
try{
floor1();
} catch (FileNotFoundException e){
e.printStackTrace();
} catch (ParseException e){
e.printStackTrace();//打印异常信息
}
}
public static void floor1() throws ParseException,FileNotFoundException{
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
Date d = sdf.parse("2018-11-10 20:10");//1.此处格式不正确,应通过throws抛出异常
floor2();//3.承接下层异常,继续向上抛
}
public static void floor2() throws FileNotFoundException{
InputStream is = new FileInputStream(D:/123.png)//2.找不到文件,通过throws向上层抛出异常
}
//实际开发由于不知道具体异常类型,统一使用throws Exception就可以处理所有异常
2.修复异常
public static void main(){
while(true){
try{
double d = getMoney();
} catch (Exception e){
sout("输入含有非数字");//2.解决异常,让用户重新输入信息
}
}
}
public double getMoney() throws Exception{
Scanner sc = new Scanner;
while(true){
sout("请输入价格");
double price = sc.nextDouble();//1.假如输入的是字符类型,会导致异常,上抛
if(price>=0)
return price;
else{
sout("输入不合法");
}
}
}
集合框架
基本集合体系结构分为单列集合Collection(单值)和双列集合Map(键值对)
Collection
Collection本身是一个接口,其具有两个主要子接口List,Set。根据这两个接口又向下实现了很多实现类如ArrayList,LinkedList,HashSet.其中:List系列的集合具有元素有序,可重复,有索引的特点,而Set系列的集合元素无序,不重复,无索引。
Collection常用方法
//1.构造方法
//Collection自身作为接口,本身不能创造对象,但其子类都需要重写其抽象方法。
Collection<String> c = new ArrayList<>;
//2.常见方法
//添加元素
add(E e);
addAll(Collection<? extends T> c2);//将c2的所有元素添加到主调c
//清空元素
clear();
//删除元素
remove(E e);
//是否包含某对象
contain(Object obj);
//是否为空
isEmpty();
//返回元素个数
size();
//把集合元素存入数组
public Object[] toArray();
String[] s = c.toArray(new String[c.size()]);//指定返回数组类型
Collection遍历方式
//1.迭代器
//通过Iterator来遍历,详见api-Iterator
Iterator<String> it = c.iterator();
while(it.hasNext()){
String str = it.next();
sout(str);
}
//2.增强for循环
//格式:for(数据类型 变量名:数组或集合){}
for(String ele:c){
sout(ele);
}
//3.Lambda表达式
//Collection提供了一个默认方法用于遍历forEach
default void forEach(Consumer<? super T> action){}
//源码
c.forEach(new Consumer<String>(){
@Override
public void accept(String s){
System.out.println(s);
}
});
//Lambda简化
c.forEach(s->System.out.println(s));
//静态方法引用
c.forEach(System.out::println);
List常用方法
同理,List的方法也都能被ArrayList和LinkedList调用
//指定位置插入元素
add(int index,E e);
//删除指定索引元素并返回被删除元素
E remove(int index);
//修改指定索引元素并返回被修改元素
E set (int index,E e);
//返回指定索引元素
get(int index);
List遍历方式
List<String> list = new ArrayList<>();
//1.for循环(因为有索引)
int size=list.size();
for(int i=0;i<size;i++){}
//2.迭代器
Iterator<String> iterator = list.iterator();
//3.增强for循环
for(String s:list){
System.out.println(s);
}
//4.Lambda表达式
list.forEach(s->System.out.println(s));
//方法引用
list.forEach(System.out::println);
ArrayList,LinkedList的区别
二者最主要的区别就是底层实现逻辑不同:
-
ArrayList:基于数组实现,可以直接使用索引进行查询,查询速度快,但是每删除一个数据都需要将后面的数据向前移,其删除效率低,添加效率更低,因为每次添加数据,需要先将后面的数据后移,有时还需扩容
-
LinkedList:基于双向链表实现,每次查询都需要从头遍历到索引位置,查询效率相对低,但增删速度都相对较快,删除方法如下,增加同理
//定义结点 class node{ int val; node next; node(int val){ this.val=val; this.next=null; } } //设有链表1->2->3->4,现在要删除val=3的元素 //提供头结点head node cur = head; //找到3结点的前一结点2 while(cur->next->val!=3){ cur=cur->next; } //将3结点移除链表 node p = cur->next->next; cur->next->next = null; cur->next = p;
也因此,LinkedList增加了很多首尾操作的新方法
//开头插入指定元素 addFirst(E e); //结尾插入指定元素 addLast(E e); //返回第一个元素 getFirst(); //返回最后一个元素 getLast(); //删除第一个元素 removeFirst(); //删除最后一个元素 removeLast();
Set
整体特点是无序,不重复,无索引,但不同的集合有不同的特点,set自身没有额外方法,基本方法均由Collection提供
哈希值
是一个int类型的数值,java中每个对象都有其自己的哈希值
//返回哈希值
public int hashCode();
特点:同一个对象多次调用hashCode得到的值是相同的,不同的对象其哈希值一般不同,但有时会相同,产生哈希碰撞。
HashSet
基于哈希表实现,在jdk8之前,哈希表是通过数组+链表(拉链法)实现的,增删改查效率都较高
- 创建一个默认长度为16的数组,默认加载因子为0.75(当数组已经占满16*0.75=12时,就会自动进行扩容,一般默认是原数组大小的两倍,然后再将所有值重新分配位置)
- 使用元素的哈希值对数组长度求余来计算存入位置
- 若该位置为null,直接存入
- 若已经有元素存入,进行equals比较,若相等,则不存入,若不相等,则将新数据存入,然后通过拉链表,将旧元素往外拉长为链表
当数据过多时会导致链表过长,导致效率降低,在jdk8之后,当链表长度超过8且数组长度>=64时,会将链表转为红黑树进行实现。
HashSet不能对对象不同,但内容相同的数据去重。若想实现内容相同也去重,需要重写hashCode和equals方法
LinkedHashSet
基于哈希表实现,同时每个元素使用一个双链表来记录他的前后元素,使其还具有有序性,同时也有set的不重复,无索引的特点
TreeSet
特点:不重复,无索引,可排序(可指定升序或降序),其排序性通过红黑树实现。对于Integer和Double类型默认按照数值本身排序,对于字符串默认按照字符编号排序,对于自定义变量直接加入TreeSet会因无法排序报错,有两种解决方法
//在自定义类中实现Comparable接口,并重写compareTo方法,自定义比较规则
public class Student implements Comparable{
@Override
public int compareTo(Student o){
return this.age-o.age;//若相等,由于Set特性,会导致数据丢失,此时可以通过设置if分支,使相等直接返回1或-1来解决
}
}
//通过TreeSet集合有参构造器,设置Comparator对象
Set<Student> students = new TreeSet<>(new Comparator<Student>(){
@Override
public int compare(Student o1,Student o2){
return Double.compare(o1.getHeight(),o2.getHeight());
}
});
可变参数
格式:数据类型…参数名称。具有灵活接受数据的特点,它可以接受一个或多个数据或不传数据甚至直接传一个数组。通常定义在方法和构造器的形参列表中。
public static void test(int...nums){
sout(nums.length);
sout(Arrays.toString(nums))
}
test();
test(10,20);
test(new int[10,20,30]);
//注意:一个形参列表只能有一个可变形参,并且必须放在形参列表的最后面
Map
双列集合,每次存储一个键值对Entry对象作为元素{key1=value1,key2=value2}.map集合的所有键不允许重复,但是值可以重复
Map为接口,向下有实现类HashMap,LinkedHashMap,TreeMap
Map常用方法
//1.构造方法
Map<String,Integer> map = new HashMap<>();
//2.常用方法
//添加元素
put(K key,V value);//假如新添加的键值已存在,则会覆盖以前的键值对,添加新值
//获取集合大小
size();
//清空集合
clear();
//判断集合是否为空
isEmpty();
//根据键获取值
get(Object key);
//删除元素
remove(Object key);
//判断是否包含某个键
containsKey(Object key);
//判断是否包含某个键
containsValue(Object Value);
//获取全部键的集合
Set<k> keySet();
//获取map集合的全部值
Collection<V> values();
Map遍历方法
//1.键找值
Map<String,Integer> map = new HashMap<>();
map.put("a", 1);
Set<String> set = map.keySet();
for(String s : set) {
System.out.println(s+" "+map.get(s));
}
//2.键值对
//使用Map提供的entrySet方法,把map集合转换为键值对类型的Set集合
Set<Map.Entry<K,V>> entrySet();
Set<Map.Entry<String,Integer>> entris = map.entrySet();
for(Map.Entry<String,Integer> entry:entris){
System.out.println(entry.getKey()+" "+entry.getValue());
}
//3.lambda表达式
//使用Map提供的forEach方法
forEach(BiConsumer<? super K,? super V> action);
//原码
map.forEach(new BiConsumer<String,Integer>() {
@Override
public void accept(String s, Integer integer) {
System.out.println(s+" "+integer);
}
});
//lambda表达式
map.forEach((s,integer)->System.out.println(s+" "+integer));
HashMap
特点:有序,不重复,无索引,底层基于哈希表实现。和HashSet原理一致,只不过每个键多对应了一个值,存储的数据类型变成了键值对。同HashSet,若自定义对象类型,要对对象不同,内容相同的元素去重,需要重写hashCode和equals方法
@Override
public boolean equals(Object o){
if(this == o)return true;
if (o == null || getClass() != o.getClass()) return false;
Student student = (Student) o;
return age == student.age && Objects.equals(name, student.name);
}
@Override
public int hashCode() {
return Objects.hash(name, age);//需要根据对象的内容来生成hash值
}
LinkedHashMap
同LinkedHashSet,有序,不重复,无索引。存储元素变为键值对
TreeMap
底层实现同TreeSet,使用红黑树实现,不重复,无索引,可排序,但只能根据键来排序。同样支持让类实现Comparable接口,或通过TreeMap提供的有参构造器来指定比较规则。
集合嵌套
可以将集合作为泛型嵌套进集合中,构成多维集合
//二维数组
ArrayList<ArrayList<String>> arrs;
Stream流
支持链式编程,jdk8之后新增操作,主要是用于处理集合元素
//1.获取Stream流
//获取集合的Stream流,Collection提供了Stream方法
default Stream<E> stream();
//对于Map集合,可以分别存储键值对,或使用entrySet方法,把map集合转换为键值对类型的Set集合
Set<Map.Entry<String,Double>> entries = map.emtrySet()
Stream<Map.Entry<String,Double>> s = entries.stream;
//获取数组的Stream流
Arrays.stream(T[] arr);
Stream.of(T[] arr);
//2.Stream流的中间方法,每次调用会返回新的Stream流,可以再次调用
//对数据元素进行过滤
filter(Predicate<? super T> predicate);//实际运用中间是一个lambda表达式形似(s -> s >= 60)
//排序(指定规则)
sorted();
sorted(Comparator<? super T> comparator);
//获取前几个元素
limit(long maxSize);
//跳过前几个元素
skip(long n);
//去除重复元素,要是不同对象,内容重复,需要重写对象的hashcode和equals方法
distinct();
//映射加工,接受一个函数式接口,把流中的数据加工为另一个数据类型
<R>Stream<R> map(Function<? super T,? extends R) mapper;//实例:map(s->s.getName());
//合并流
concat(Stream a,Stream b);
//3.Stream流的终结方法,调用后不再产生新的Stream流
//遍历
forEach(Consumer action);
//统计流运算后的元素个数
count();
//获取最大值元素
max(Comparator<? super T> comparator);
//获取最小值元素
min(Comparator<? super T> comparator);
//收集流
R collect(Collector collector);//Collector具有toSet,toList,toMap等方法
Object[] toArray();
IO流
File
File对象构造
File类的对象用于操作当前操作系统的文件或文件夹,但File只能对文件本身进行操作,不能读写文件里面存储的数据
//1.创建File类对象
//根据文件路径创建文件对象
File(String pathname);
//根据父路径和子路径名字创建文件对象
File(String parent,String child);
//根据父路径对应文件对象和子路径名字创建文件对象
File(File parent,String child);
//总之就是前者是路径,后者是名字
文件格式说明:可以用正斜杠/来进行路径分割,也可用反斜杠,但需要转义,所以需要用双斜杠\\,也可以用分隔符separator
File f1 = new File("D:\\resource\\ab.txt");
File f1 = new File("D:/resource/ab.txt");
File f1 = new File("D:"+File.separator+"resource"+File.separator+"ab.txt");
File对象可以指向一个不存在的文件路径,但返回文件大小为0,内容不存在,可以使用exists方法来判断文件是否存在
boolean exist = f1.exists();
绝对路径与相对路径
上述路径为绝对路径(从盘符到文件的全路径),在多人拷贝模块套用程序时不方便。在同一个模块中存储的文件,调用文件可以使用相对路径(从工作目录(菜单->运行->编辑配置->工作目录)到文件的路径)。
"D:\JAVA\javaproject\untitled\untitled1\src\hello\hello.java";//绝对路径
"untitled1\src\hello\hello.java";//相对路径中使用..\可以返回上一级目录
File常用方法
//2.常用方法
"获取文件信息"
//文件是否存在
exists();
//判断当前文件对象对应路径是否是文件
isFile();
//判断当前文件对象是否是文件夹
isDirectory();
//获取文件名称包括后缀
getName();
//文件改名
renameTo(File)
//获取文件大小,返回字节个数
length();
//获取文件的最后修改时间
lastModified();
//获取创建文件时使用的路径
getPath();
//获取绝对路径
getAbsolutePath();
"创建删除文件(创建文件需要File对象的地址未被使用)"
//创建新文件
createNewFile();
//创建文件夹(一级)
mkdir();
//创建文件夹(多级)
mkdirs();
//删除文件,文件夹,不能删除非空文件夹
delete();
"遍历文件夹"
//返回当前目录下所有的一级文件名称到一个字符串数组
list();
//获取当前目录下所有的一级文件对象到一个文件对象数组
listFiles();
//注意:使用listFiles时,当主调是文件或者路径不存在时,返回null;当主调时空文件夹时,返回一个长度为0的数组;当主调是一个有内容的文件夹时,将里面所有的一级文件和文件夹放在File数组中返回。
字符集
- ACSII字符集:只有英文数字和符号,占一个字节
- GBK字符集:汉字占两个字节,英文,数字占一个字节
- UTF-8字符集:汉字占三个字节,英文和数字占一个字节
java中String提供了对字符进行编码和解码操作
//平台默认字符集编码
byte[] getBytes();
//指定字符集编码
byte[] getBytes(String charsetName);
//解码
String(byte[] bytes);
String(byte[],String charsetName);
字符流
用于读写文件或者是网络中的数据,流主要有四大类,其中字节类适合操作所有文件,字符类一般只适合操作文本文件。IO流体系总共提供了四个抽象类:InputStream,OutputStream,Reader,Writer。其对应实现类分别如下:
FileInputStream-文件字节输入流
以内存为基准,将磁盘文件数据以字节的形式存入内存,读取性能较差,并且读取汉字会出现乱码
//1.构造方法
FileInputStream(File file);
FileInputStream(String pathname);
//2.常用方法
//读取一个字节,若没有字节可读取,返回-1
read();
//每次用一个字节数组区读取,返回字节数组读取了多少个字节
read(byte[] buffer);
//关闭流,释放资源
close();
//将当前字节输入流对应的文件对象的字节数据据装到一个字节数组返回
byte[] readAllBytes();
使用字节数组读取数据时,会产生覆盖现象
//txt:abc66
InputString is = new FileInputStream("txt");
byte[] buffer = new byte[3];
//第一次
int len = is.read(buffer);
string rs = new String(buffer);
sout(rs);//abc
sout(len);//3
//第二次
int lens2 = is.read(buffer);
string rs2 = new String(buffer);
sout(rs2);//66c,此处的c由于剩余数据不足,未能覆盖
sout(len2);//2
//第三次
int lens3 = is.read(buffer);
string rs3 = new String(buffer);
sout(rs3);//66c
sout(len2);//-1
//改造
int len;
while((len=is.read(buffer))!=-1)
{
String rs = new String(buffer,0,len);
sout(rs);k
}//abc66
//这种情况下读取中文仍会出现乱码
//改造2.0
byte[] buffer = is.readAllBytes();
sout(new String(buffer));
//这种情况没有乱码
FileOutputStream-文件字节输出流
以内存为基准,把内存中的数据以字节的形式写到文件中
//1.构造方法
FileOutputStream(File file);
FileOutputStream(String filepath);
//可追加数据
FileOutputStream(File file,boolean append);
FileOutputStream(String filepath,boolean append);
//2.常用方法
//写一个字节
write(int a);
//写一个字节数组
write(byte[] buffer);
//从指定位置写一个长度为len的数组
write(byte[] buffer,int pos,int len);
//关闭流
close();
try-catch-finally
无论如何都会执行一次finally,避免出现异常,内存得不到释放(除非虚拟机关闭)
try{
OutputStream os = FileOutputStream(filepath);
}catch(exception e){
e.printStackTrace();
}finally{
is.close();//无论如何都会执行
}
try-with-resource
jdk7以后提供的更简洁的方案
try(定义资源1,定义资源2){
...
}catch{
...
}
//资源是指实现了AutoCloseable的实现类
FileReader-文件字符输入流
专门用于对文本内容的读取,单个字符读取不会出现乱码
//1.构造方法
FileReader(File file);
FileReader(String pathname);
//2.常用方法
//读取一个字符
read();
//用字符数组读取自读
read(char[] buffer);
FileWriter-文件字符输出流
//1.构造方法
FileWriter(File file);
FileWriter(String filepath);
//可追加数据
FileWriter(File file,boolean append);
FileWriter(String filepath,boolean append);
//2.常用方法
write(int c);
write(String str);
write(String str,int off,int len);
write(char[] cbuf);
write(char[] cbuf,int off,int len);
//刷新流
flush();
字符流写出数据后,必须先刷新流,或者关闭流,写出的数据才能生效
缓冲流
用于对原始流进行包装,提高原始流读写数据的性能。缓冲流自带8KB的缓冲池
//1.构造方法
//包装低级IO流
//字节缓冲输入流
BufferedInputStream(InputStream is);
//字节缓冲输出流
BufferedOutputStream(OutputStream os);
//字符缓冲输入流
BufferedReader(Reader r);
//字符缓冲输出流
BufferedWriter(Writer w);
//2.常用方法
//字符缓冲输入流新增功能:按行读取
readLine();
//字符缓冲输出流新增功能:换行
newLine();
性能分析:当字节数组设置大于8KB时,是否使用缓冲流影响不大
转换流
用于解决不同编码时,字符流读取文本内容乱码的问题
//1.构造方法
//字符转换输入流
InputStreamReader(InputStream is,String charset);
//字符转换输出流
OnputStreamReader(OnputStream os,String charset);
打印流
提供更方便高效的打印方法
//1.构造方法
//直接通向输出流/文件/文件路径
PrintStream(OutputStream/File/String);
//可指定字符编码
PrintStream(String FileName,Charset charset);"Charset.forName("GBK")"
//可实现自动刷新
PrintStream(OutputStream out,boolean autoFlush);
PrintStream(OutputStream out,boolean autoFlush,String encoding);
//2.常用方法
//打印数据
public void println(xx);
//打印字节
public void write(int/byte[]);
还有个打印流是PrintWriter,与PrintStream几乎无差别:
- 构造方法多一种使用Writer对象创建:PrintWriter(OutputStream/Writer/File/String);
- write方法提供输出字符功能:write(int/String);
注意:高级流不能直接追加数据,但是可以通过包装低级流的方式来追加:PrintStream(new FileOutputStream(String,true));
补充-更改System.out.println()的输出位置:
PrintStream ps= new PrintStream(String Filepath);
System.setOut(ps);//把系统默认打印流更改为自己设置的打印位置
数据流
允许把数据和其类型一并写出去
//1.构造方法
DataOutputStream(OutputStream out);
//2.常用方法
//不同数据类型写入
writeByte(int v);
writeInt(int v);
writeDouble(Double v);
//将字符串数据以UFT-8编码为字节写入
writeUTF(String str);
//基础写字节
write(int/byte[])
同理有读取流
//1.构造方法
DataInputStream(InputStream out);
//2.常用方法
//不同数据类型读取
readByte(int v);
readInt(int v);
readDouble(Double v);
//将字符串数据以UFT-8编码为字节读取
readUTF(String str);
//基础读字节
read(int/byte[])
序列化流
对象序列化(把对象写入到文件中)
//1.构造方法
ObjectOutputStream(OutputStream out);
//2.常用方法
//写入一个对象
writeObject(Object o);
//注意:对象需要序列化必须实现序列化接口
public class User implements Serializable{}
对象反序列化(把对象从文件中读出来)
//1.构造方法
ObjectInputStream(InputStream in);
//2.常用方法
//写入一个对象
readObject(Object o);
如果需要一次性序列化多个对象,使用ArrayList依次进行即可,ArrayList已经实现了序列化接口
IO框架
框架,是一类接口和类整合起来的用于处理某类问题的整合包,通常后缀名为.jar。
//FileUtils类提供的部分静态方法
//复制文件
copyFile(File srcFile,File destFile);
//复制文件夹
copyDirectory(File srcDir,File destDir);
//删除文件夹
deleteDirectory(File directory);
//读数据
readFileToString(File file,String encoding);
//写数据
writeStringToFile(File file,String data,String charname,boolean append);
//IOUtils类提供的部分静态方法
//复制文件
copy(InputStream in,OutputStream out);
copy(Reader r,Writer w);
//写数据
write(String data,OutputStream out,String charname);
//Java原生代码Files类提供静态方法(功能略少于第三方框架,做了解)
//复制文件
copy(Path.of(String srcPath),Path.of(String destPath));
//读取数据
readString(Path.of(String path));
特殊文件与日志
特殊文件
根据后缀名不同,具有不同的特殊文件,不同的特殊文件有不同的格式。常见的文件有:普通文件.txt,属性文件.properties,XML文件.xml
properties
数据均为键值对,且键不能重复。可以使用Map集合中的Properties来进行属性文件内容的读写。
//1.构造方法
Properties();
//2.常用方法
//读取属性文件中的键值对
load(InputStream is);
load(Reader reader);
//根据键获取值
getProperty(String key);
//获取全部键的集合
Set<String> stringPropertyNames();
//保存键值对数据到properties对象
setProperty(String key,String value);
//写出到属性文件
store(OutputStream os,String comments);//comments是备注
store(Writer w,String comments);
propeties也支持通过lambda表达式调用propeties方法
XML
推荐使用StringBuilder来拼接出XML格式的数据,再通过缓冲流写入。读取数据也同样只推荐使用readLine进行读取。关于XML的约束目前仅了解
日志
Logback快速入门
最常见的是使用Logback框架实现日志技术,其实现的接口是SLF4J(Simple Logging Facade for Java(SLF4J))。想使用Logback日志框架,必须在项目中整合:slf4j-api:日志接口,logback-core,logback-classic。
-
导入Logback框架到项目中
-
将logback.xml文件直接拷贝到src目录下
-
创建Logback框架提供的Logger对象,然后用对象调用其提供的方法来记录系统的日志信息
public static final Logger LOGGER = loggerFactory.getLogger(String name);
-
使用logger提供的方法进行日志记录
核心配置文件-logback.xml
用于对Logback日志框架进行控制。
- appender负责管理日志输出位置:FILE(文件),CONSOLE(控制台)
- encoder负责控制日志输出格式:其中用pattern字符串进行规范
- file控制日志输出地址
- rollingPolicy指定日志的拆分和压缩规则:fileNamePattern用来指定压缩文件名称来确定分割方式,maxFileSize指定大小
- root level控制日志的开启和关闭:ALL(开启),OFF(关闭)
Logback设置日志级别
日志都会分级别,常见日志级别有
级别 | 说明 |
---|---|
trace | 追踪,指明日志运行轨迹 |
debug | 调试,一般作为最低级别 |
info | 输出重要运行信息,数据连接,网络连接,io操作等 |
warn | 警告信息,可能会出现错误 |
error | 错误信息 |
在root level中可设置级别,只有当日志级别大于等于级别时才会打印
多线程
多线程是指由CPU进行调度执行,实现的多条执行流程的技术。Java中是通过Thread类的对象来代表线程的。
多线程的创建方式
有三种方法可以创建新的执行线程:
-
将类声明为Thread的子类,且重写run方法
public class myThread extends Thread{ @Override public void run(){} } Thread t = new myThread(); t.start(); //注意,主程序在调用时,应调用start方法来开始线程。不能把主线程的任务放在启动子线程之前。 //优点:编译简单 //缺点:无法继承其他类,灵活性差
-
声明一个实现Runnable接口的类,然后实现run方法
public class myRunnable implements Runnable{ @Override public void run(){}; } Runnable r = new myRunnable(); new Thread(r).start();//将任务对象封装为线程对象 //优点:可以继承其他类和实现其他接口,扩展性强 //缺点:需要多一个Runnable对象 //使用匿名内部类优化,直接创建Runnable接口的任务对象 Runnable r = new Runnable(){ @Override public void run(){ for(int i =1;i<5;i++){ System.out.println("输出"+i); } } } new Thread(r).start(); //也可以简化为: new Thread(new Runnable(){ @Override public void run(){ for(int i =1;i<5;i++){ System.out.println("输出"+i); } } }).start(); //简化为lambda表达式 new Thread(()->{ for(int i =1;i<5;i++){ System.out.println("输出"+i); } }).start();
-
使用Callable接口和FutureTask类来实现,需要重写call方法,可以返回线程执行完毕的结果
public class myCallable implements Callable<String>{ @Override public String call() throws Exception{ return null; } } Callable<String> call = new myCallable(); FutureTask<String> ft = new FutureTask<>(); new Thread(ft).start(); String rs = ft.get();//获取线程执行完毕的结果,需要解决异常(try catch?) //优点:一定能拿到线程执行完返回的结果,扩展性好 //缺点:编码较复杂
Thread
下面提供Thread线程对象的一些常用方法
//1.构造方法
//指定线程名称
Thread(String name);
//封装Runnable对象为线程对象
Thread(Runnable target);
//结合
Thread(Runnable target,String name);
//2.常用方法
//线程任务方法
run();
//启动线程
start();
//获取当前线程名称
getName();
//为线程设置名称
setName(String name);
//获取当前执行的线程对象
static Thread currentThread();
//让当前执行的线程休眠
static void sleep(long time);
//让调用方法的线程优先执行,执行完毕再继续当前线程
join();
线程同步
为解决多线程同时调用并修改资源,导致的数据异常,所产生的方案。主要的思想是让多个线程实现先后依次地来访问共享资源,使用锁可以完成。
方法一:同步代码块
把访问共享资源的代码上锁,以此保证线程安全。
//用synchronized来对代码块上锁
synchronized(object){
this.getMoney(money);
}
//object必须是仅一份的资源,"字符串",常量,或是其他仅存一份的资源,推荐使用this作为锁,用于对当前类访问的限制,对于静态方法,一般使用类名.class
方法二:同步方法
把访问资源的核心方法上锁。
public synchronized void drawMoney(double money){
this.getMoney(money);
}
//隐含锁是this,锁的范围是整个方法。不同的实例对象有各自独立的锁,所以多个线程访问不同实例对象的同步方法或代码块时,不会相互阻塞。
//对于静态类,整个类只有一个类锁,所以当多个线程访问同一个类的静态同步方法或代码块时,它们会相互阻塞。
//同一个类中,假如对多个方法上了同一个锁,那么多个线程调用不同方法时,也会被阻塞
方法三:Lock锁
JDK5后的新操作,更灵活,更方便。Lock是接口,需要使用他的实现类ReentrantLock来构建Lock锁对象。
//推荐每个账户对象在初始化时自动生成一个专属的锁对象
private final Lock lk = new ReentrantLock();
//上锁
lk.lock();
this.getMoney(money);
//解锁
lk.unlock();
//相比前两种方式更灵活,缺点:为解决出现异常导致的无法解锁,需要使用try-catch-finally来包住代码
try{
lk.lock();
this.getMoney(money);
}catch(Exception e){
e.printStackTrace();
}finally{
lk.unlock();
}
线程通信
指各个线程之间互相交流,传递讯息,合理分配锁。有时,当使用sleep方法时,会同时调用等待方法,释放所占锁,使得资源能被最大限度利用
//等待,并释放所占锁
wait();
//唤醒正在等待的单个线程
notify();
//唤醒所有线程
notifyAll();
线程池
是一个可以复用线程的池化技术。如果不使用线程池,每有一个请求,就会创建出一个新的线程,开销较大。线程池工作原理。存在一个固定的工作线程空间,里面已经存放了一定数量的线程对象,称为工作线程(WorkQueue)。所有需要调用线程对象的任务都会进入一个队列,称为任务队列(WorkQueue)。工作时,先进队的任务分配线程对象,当分配完毕时,未被分配线程的任务则会在原地等待,称为阻塞,当拿到线程的任务完成任务之后,则会释放线程对象,继续分配给下一个任务,实现线程的重复利用。
在JDK5以后,提供了ExecutorService接口来代表线程池,其最常用的实现类是:ThreadPoolExecutor
创建线程池
//1.构造方法
public ThreadPoolExecutor(int corePoolSize,int maximumPoolSize,long keepAliveTime,TimeUnit unit,
BlockingQueue<Runnable> workQueue,ThreadFactory threadfactory,
RejectedExecutionHandler handler);
//corePoolSize:线程池核心线程的数量(可复用线程)
//maximumPoolSize:指定线程池的最大线程数量(临时线程+核心线程)
//keepAliveTime:临时线程的存活时间
//TimeUnit unit:存活时间的单位(秒,分,时,天)
//BlockingQueue<Runnable> workQueue:指定线程池的任务队列
//ThreadFactory threadfactory:指定线程池的线程工厂(用于创建线程)
//RejectedExecutionHandler handler:指定线程池的任务拒绝策略(线程均满,任务队列满,如何处理)
//创建实例:
ExecutorService pool = new ThreadPoolExecutor(3,5,8,
TimeUnit.SECONDS,new ArrayBlockingQueue<Runnable>(4), Executors.defaultThreadFactory(),
new ThreadPoolExecutor.AbortPolicy());
//Executors是一个工具类,Executors.defaultThreadFactory()会获取一个默认的线程工厂
//AbortPolicy是ThreadPoolExecutor中的内部类,实际是RejectedExecutionHandler接口的实现类对象
//注意:1.只有当核心线程在忙,且任务队列已满的时候,并且可以创建临时线程,才会创建临时线程 2.只有当核心线程和临时线程都在忙,任务队列已经满了,才会开始拒绝任务
//线程池不会自动关闭,长久存在,如果需要关闭线程池需要调用以下方法
//等所有任务结束后,关闭线程池
shutdown();
//立即关闭线程池,返回未执行完的任务列表
List<Runnable> shutdownNow();
//新任务拒绝策略:
//丢弃任务并抛出异常
ThreadPoolExecutor.AbortPolicy();
//丢弃任务,但是不抛出异常
ThreadPoolExecutor.DiscardPolicy();
//抛弃队列中等待最久的任务,加入当前任务
ThreadPoolExecutor.DiscardOldestPolicy();
//由主线程调用run方法,绕过线程池直接执行
ThreadPoolExecutor.CallerRunsPolicy();
线程池处理Runnable任务
//执行Runnable任务
execute(Runnable command);
Runnable target = new myRunnable();
pool.execute(target);
线程池处理Callable任务
//执行任务,返回未来任务对象获取线程结果
Future<T> submit(callable<T> task);
Future<String> f = pool.submit(new myCallable());
sout(f.get());
Executors创建线程池
//创建固定数量线程池,如果某个线程因异常结束,则会自动补充一个新的核心线程
ExecutorService newFixedThreadPool(int nThreads);
//创建一个核心线程的线程池,如果线程因异常结束,则会自动补充一个新的核心线程
ExecutorService newSingleThreadExecutor();
//线程数量随任务增加而增加,如果线程任务执行完毕并空闲了60s,则会被回收
ExecutorService newCachedThreadPool();
//创建一个线程池,可以实现在给定的时间后或者定期实现任务
ExecutorService newScheduledThreadPool();
//注意:大型并发系统环境中,使用Executors可能会出现系统风险:newFixedThreadPool和newSingleThreadExecutor允许请求的任务长度为Integer.MAX_VALUE,会导致过多任务堆积。newCachedThreadPool会导致线程创建过多,内存占用过大
并发,并行
进程:每个正在运行的软件都是一个独立的进程,线程属于进程,一个进程中可以同时运行多个线程
并发:进程中的线程是由CPU负责调度执行的,但是CPU能同时处理线程的数量有限,为保证全部线程都在往前执行,CPU会轮询为每个线程服务,由于CPU切换的速度很快,看上去就是所有线程都在同步执行,这称为并发
并行:在同一时刻上,同时有多个线程被CPU调度,能够同时调度的线程的数量由CPU性能决定
多线程是由并发和并行共同实现的
线程的生命周期
java中,共定义了6中线程状态:
- New:新建状态
- Runnable:可运行状态,New状态调用start()方法得到
- Teminated:被终止状态,出现异常或正常执行完毕
- Blocked:锁阻塞状态,未能获得锁对象
- Waiting:无限等待状态,获得锁对象并调用了wait()方法得到,被notify后会继续抢锁来决定之后的状态
- Time Waiting:计时等待状态,sleep(t)或wait(t)得到,被notify或等待时间结束后会继续抢锁来决定之后的状态
乐观锁
之前学的锁称为悲观锁,这种锁会在执行关键代码时就加锁,只能一个线程进入,等到访问完毕时才解锁。乐观锁,是指一开始不上锁,直到即将要出现安全问题的时候才开始加锁控制,同样能够保证线程安全且性能较好。
乐观锁原理:使用CAS算法,每次进行数据修改时,先记录数据原本的值,在修改完数据后,存储前,先检查此时数据的值是否被改变,没有则更改数据,若发现数据已被改变,则此次操作作废,重新取新数据重复上述过程。
//java中提供由乐观锁实现的原子类
AtomicInteger();
AtomicLong();
...
网络通信
网络通信概述
基本通信架构有两种:CS架构(Client-Server)和BS架构(Browser-Server)。网络通信三要素:IP地址,端口,协议。特殊IP地址:127.0.0.1,代表本机IP,只会寻找当前所在的主机。常用指令:ipconfig:查看本机IP地址,ping ip地址:检查网络是否连接
IP地址
java中使用InetAddress类来代表ip地址
//1.构造方法
//获取本机ip,返回一个InetAddress对象
static InetAddress getLocalHost();
//根据ip或域名返回一个InetAddress对象
static InetAddress getByName(String host);
//2.常用方法
//获取对象对应主机名
getHostName();
//获取对象ip地址信息
getHostAddress();
//在指定毫秒内,判断主机与该ip对应的主机是否能连通
isReachable(int timeout);
端口
标记计算机设备上运行的应用程序,被规定为一个16位数,0-65535。端口由三个区段:
- 周知端口:0-1023,被预先定义的知名应用占用(HTTP为80)
- 注册端口:1024-49151,分配给用户或是某些应用程序
- 动态端口:49152-65535,用于动态分配进程
个人开发通常使用注册端口,且一个设备中不允许出现两个程序端口号一样
协议
两种网络模型:OSI和TCP/IP,此处主要关注传输层的协议
- UDP用户数据包协议:是不可靠,无连接通信,详细见计网吧。。
- TCP传输控制协议:可靠通信,三次握手,四次挥手
UDP通信
使用DatagramSocket来实现客户端和服务端的创建
//1.构造方法
//创建客户端的Socket对象,并随机分配端口号
DatagramSocket();
//创建服务端的Socket对象,并指定端口号
DatagramSocket(int port);
//2.常用方法
//发送数据包
send(DategramPacket dp);
//使用数据包接收数据
receive(DatagramPacket p);
//释放socket
close();
使用DatagramPacket来创建数据包
//1.构造方法
//创建发送数据包
DatagramPacket(byte[] buf,int length,IntAddress address,int port);
//创建接收数据包
DatagramPacket(byte[] buf,int length);
//2.常用方法
//获取数据包实际收到的字节个数
getLength();
TCP通信
面向连接,可靠通信
客户端通信
java中依靠Socket类来实现TCP通信的客户端
//1.构造方法
//根据指定服务器ip和端口号与服务器建立连接,获取Socket对象
Socket(String host,int port);
//2.常用方法
//获得字节输出流对象
getOutputStream();
//获得字节输入流对象
getInputStream();
//客户端实例:
//创建Socket对象,请求与服务端连接
Socket socket = new Socket(InetAddress.getLocalHost().getHostAddress(),8888);
//从socket通信管道中获得一个字节输出流,用于发数据给服务端程序
OutputStream outputStream = socket.getOutputStream();
//包装数据流
DataOutputStream dos = new DataOutputStream(outputStream);
//发送数据
dos.writeUTF("Hello Client");
//释放资源,也可以使用try-with或try-catch-finally来释放
dos.close();
socket.close();
服务端通信
java提供ServerSocket类来实现服务端的程序开发
//1.构造方法
//为服务端程序注册端口
ServerSocket(int port);
//阻塞等待客户端连接请求,连接成功则返回服务端这边的Socket对象
Socket accept();
//2.常用方法
//获取客户端地址
getRemoteSocketAddress();
//服务端实例:
//创建ServerSocket对象,为服务端注册端口
ServerSocket severSocket = new SerberSocket(8888);
//使用对象调用accept方法,来等待接收客户端的连接请求
Socket socket = serverSocket.accept();
//从socket信道得到字节输入流
InputStream is = socket.getInputStream();
//包装输入流
DataInputStream dis = new DataInputStream(is);
//读取客户端数据
String rs = dis.readUTF();
多发多传实现
客户端:
Scanner sc = new Scanner(System.in);
while(true){
String msg = sc.nextLine();
//退出机制
if(msg.equals("exit")){
sout("已退出")
dos.close();
socket.close();
break;
}
dos.writeUTF(msg);
dos.flush();//刷新数据
}
服务端:
while(true){
//当客户端关闭通道时服务端会出现异常,使用try-catch解决并提醒客户端已guan'bi
try{
String rs = dis.readUTF();
sout(rs);
}catch(Exception e){
sout("客户端已离线");
dis.close();
socket.close();
break;
}
}
单元测试
用于对方法进行测试验证,第三方提供junit框架用来进行单元测试,测试步骤基本如下:
- 导入junit的jar包到项目中
- 为业务类定义对应的测试类,编写对应的测试方法(公开,无参,无返回值)
- 测试方法上必须声明@Test注解,add Junit4.
- 编写测试方法
//业务类
public class StringUtil{
public static void printNumber(String name){
if(name==null){
system.out.println(0);
return;
}
system.out.println("名字长度"+name.length());
}
}
//测试类
public class StringUtilTest{
@Test
public void testPrintNumber(){
StringUtil.printNumber("admin");
StringUtil.printNumber(null);
}
}
Junit还提供断言方法,用来测试结果是否与预测值一致
Assert.assertEquals(String message,expected,pridict);
常用注解:
//修饰实例方法,该方法会在每个测试方法执行前执行一次
@Before
//修饰实例方法,该方法会在每个测试方法执行后执行一次
@After
//修饰静态方法,该方法会在所有测试方法执行前只执行一次
@BeforeClass
//修饰静态方法,该方法会在所有测试方法执行后只执行一次
@AfterClass
反射
反射用于加载类,并允许以编程的形式来剖析类中的成分(成员变量,方法,构造器)
获取Class对象
//方法1:Class c1 = 类名.class
Class c1 = student.class;
//方法2:调用Class方法Class.forName(String package)
Class c2 = Class.forName(path);
//方法3:Object提供方法getClass()
Student s = new Student();
Class c3 = s.getClass();
获取类构造器
要获取类的构造器,首先得得到Class对象
//获取全部public构造器
getConstructors();
//获取全部构造器
getDeclaredConstructors();
//获取某个构造器,只能获取public
getConstructor(Class<?>...parameterTypes);//Class<?>...parameterTypes是形参类型写如:String.class,int.class
//获取某个构造器
getDeclaredConstructor(Class<?>...parameterTypes);
//初始化构造器对象
T newInstance(Object... initargs);//没有形参列表就是无参构造器,为支持反射的可扩展,接收时需要强转而不推荐使用泛型
//暴力反射(避免检查访问控制,即可以访问私有构造器)
setAccessible(boolean flag);//设置为true
获取类的成员变量
同理需要先取得类的Class对象
//获取全部public成员变量
getFields();
//获取全部成员变量
getDeclaredFields();
//获取某个public成员变量
getField(String name);
//获取某个成员变量
getDeclaredField(String name);
//成员变量赋值
set(Object obj,Object value);
//成员变量取值
get(Object obj);
//暴力反射
setAccessible(boolean flag);
获取类的成员方法
//获取全部public成员方法
getMethods();
//获取全部成员方法
getDeclaredMethods();
//获取某个public成员方法
getMethod(String name,Class<?>...parameterTypes);
//获取某个成员方法
getDeclaredMethod(String name,Class<?>... parameterTypes);
//执行方法
invoke(Object obj,Object... args);
//暴力反射
setAccessible(boolean flag);
注解
自定义注解
就是Java代码中的特殊标记如:@Override,java中自定义注解的方法如下:
public @interface AnnotationName{
(public) 属性类型 属性名() default 默认值;
}
//自定义测试注解
public @interface test{
String aaa();
boolean bbb() default true;
}
//注解实现
@test(aaa="hi",bbb=false)
//当注解传参只有一个值且名称为value时,value可省略
public @interface test{
String value();
boolean bbb() default true;
}
@test("hi")//bbb使用默认值
注解本质是继承了Annotation接口的接口,其中含有相应抽象方法
元注解
用于修饰注解的注解,java中只有两个元注解
Target
用于声明被修饰的注解只能在哪些位置使用
@Target(ElementType.TYPE)
1. TYPE 类或接口
2. FILED 成员变量
3. METHOD 成员方法
4. PARAMETER 方法参数
5. CONSTRUCTOR 构造器
6. LOCAL_VARIABLE 局部变量
Retention
用于声明注解的保留周期
@Retention(RetentionPolicy.RUNTIME)
1. SOURCE 只停留到源码阶段,字节码文件中不存在
2. CLASS 默认值,只停留到字节码阶段,运行阶段不存在
3. RUNTIME 最常用,一直保留到运行阶段
注解的解析
就是将代码中注解的内容解析出来,解析的思想是通过反射拿到Class,Method,Field,Constructor对象,调用AnnotatedElement接口提供的方法,进行注解解析
//获取当前对象上的注解
getDeclaredAnnotations();
//获取指定注解对象
getDeclaredAnnotation(Class<T> annotationClass);
//判断对象上是否存在某个注解
isAnnotationnPresent(Class<Annotation> annotationClass);
常用API介绍
包(package)
包是用来管理不同的程序,建包有利于方便程序管理,同一个包下的程序,可以直接互相访问,访问其他包下的程序,必须导入对应的包
//导包的组成结构为软件包名.类名
import MovieSystem.MovieOperator;
包装类
是基本数据类型经过封装后的类,属于引用数据类型,主要用于泛型调用
基本数据类型 | 包装类 |
---|---|
byte | Byte |
short | Short |
int | Integer |
long | Long |
char | Character |
float | Float |
double | Double |
boolean | Boolean |
//常用方法
//数据包装,返回一个Integer对象
Integer.valueOf(int i);
//还可以实现数据转换
Integer.valueOf(String str);
Double.valueOf(String str2);
//自动装箱
Integer a = 10;
//自动拆箱
int b = a;
ArrayList-顺序表
顺序表,长度也是大小可变
//1.构造方法
ArrayList<Integer> list = new ArrayList<Integer>();//默认初始容量为10,不加约束为泛型存储,约束的数据类型必须为封装类
//2.常见方法
//插入数据
add(int index,element);//省略index默认添加到末尾
//查询数据
get(int index);
//返回大小
size();
//删除数据
remove(int index);//返回被删除元素
remove(object);//返回布尔类型,删除第一次出现的元素
//修改数据
set(int index,element);
Arrays-数组工具类
用来操作数组的工具类
//1.常见方法
//返回数组内容
toString(int[] arr);
//拷贝数组
copyOfRange(int[] arr,int begin,int end);//指定范围
copyOf(int[] arr,int newLength);
//对数组排序
sort(int[] arr);
BigDecimal-浮点数失真
主要用于解决有时候浮点数相加失真的问题
//1.构造方法
BigDecimal(double val);//不推荐
BigDecimal(String val);
BigDecimal.valueOf(double val);//推荐
//2.常用方法
//加减乘除
add(BigDecimal a);
subtract(BigDecimal a);
mutiply(BigDecimal a);
divide(BigDecimal a);
divide(BigDecimal a,int b,enum.CONST);//后面两个参数分别为精确位数,和舍入模式(枚举量
//类型转换
doubleValue(BigDecimal b);
Calendar-日历
日历类
//1.构造方法
getInstance();//返回的是日历对象
//2.常见方法
//获取日历中的某个信息
get(int field);//field是存储在日历类里的常量,如年:Calendar.YEAR
//获取当前日历对应的日期对象
getTime();
//获取时间毫秒值
gotTimeInMills();
//修改日历的某个信息
set(int field,int value);
//为某个信息增加/减少指定值
add(int field,int amount);
Comparable-比较规则
比较规则接口,用于实现重写,实现sort排序
//构造方法
//在sort对象的类中实现Comparable接口,然后重写compareTo方法
@Override
public int compareTo(T t){
//约定:左边对象大于右边对象返回正整数,左边对象小于右边对象返回负整数,相等返回0.
if(this.age > t.age)return 1;
else if(this.age < t.age)return -1;
return 0;
}
Comparator-比较器接口
比较器接口,作用同Comparable,使用方法不同
//构造方法
//直接作为sort方法的形参,传入Comparator接口的实现类的对象
public static <T> void sort(T[] arr,Comparator<? super T> c);//sort方法
Arrays.sort(students,new Comparator<Student>() {
@Override
public int compare(Student o1, Student o2) {
return o2.getAge()-o1.getAge();
}
});
Collections-集合工具类
是一个集合的工具类,使用时直接通过类名调用
//常见方法
//为集合批量添加数据
addAll(Collection<? super T> c,T...elements);
//打乱list集合中的元素顺序
shuffle(List<?> list);
//对list集合中的元素进行升序排序
sort(List<T> list);
//对list集合中的元素按比较规则进行排序
sort(List<T> list,Comparator<? super T> c);
Date-旧·日期
jdk8以前的旧版日期api
//1.构造方法
Date();
Date(long time);//把时间毫秒值转换为日期对象
//2.常见方法
//返回时间戳
getTime();
//设置时间戳
settime(long time);
DateTimeFormatter-新·时间解析串
新版时间用于替换SimpleDateFormat,他是线程安全的
//1.构造方法
ofPattern(String pattern);
//2.常见方法
//对时间进行格式化
format(LocalDateTime now);
//解析时间
LocalDateTime.parse(String dateTimeString,DateTimeFormatter formatter);
Duration-持续时间(日-纳秒)
持续时间,计算两个时间的间隔,支持LocalTime,LocalDateTime,Instant等时间
//1.构造方法
between(startTime,endTime);
//2.常见方法
//计算时间间隔
toDays();
toHours();
toMinutes();
toSeconds();
toMillis();
toNanos();
Iterator-迭代器
本质是java框架提供的一个接口,可以通过获取迭代器的方式来遍历不同数据结构
//1.基本使用
//导入Iterator类
import java.util.Iterator;
//获取迭代器
Iterator<Integer> it = vec.iterator();//vec为一个已实例化的Vector容器,存储数据类型为Integer
//2.主要方法
//判断下一个元素是否存在
hasNext();
//返回当前元素,并将指针后移
next();
Instant-时间戳
时间戳,由两部分组成(从1970-7-1 00:00:00 到现在的总秒数+不到一秒的纳秒数)
//1.构造方法
now();
//2.常见方法
//获取总秒数
getEpochSecond();
//获取纳秒数
getNano();
//增加减少时间
plus/minusSeconds();
plus/minusNanos();
//比较时间
equals();
isBefore();
isAfter();
LocalDate(Time/DateTime)-新·日期
分别代表本地日期,本地时间,和本地日期时间,其中DateTime信息最全面,使用最多
//1.构造方法
now();//静态方法,返回对应对象
//2.常见方法
//LocalDate
//获取日期对象信息
getYear();//年
getMonthValue();//月
getDayOfMonth();//日
getDayOfYear();//一年中的第几天
getDayOfWeek();//星期几
//直接修改信息
withYear(int val);
withMonth(int val);
withDayOfMonth(int val);
withDayOfYear(int val);
//增加(减少)信息
plusYears(int val);//减用minus
plusMonths(int val);
plusDays(int val);
plusWeeks(int val);
//获取指定日期LocalDate对象
of(int year,int month,int day);
//判断两个日期是否相等,在之前,在之后
equals(LocalDate ld);
isBefore(LocalDate ld);
isAfter(LocalDate ld);
//LocalTime
"操作方法与LocalDate相同,只是操作变量不同"
Hour;
Minute;
Second;
Nano;//纳秒
//LocalDateTime
"包含信息是LocalTime和LocalDate的结合,操作方法相同"
//类型转换
toLocalDate();
toLocalTime();
of(LocalDate ld,LocalTime lt);
//反向格式化
format(DateTimeFormatter formatter);
//解析时间
parse(String datestr,DateTimeFormatter formatter);
Math-数学
静态类,提供一些数学工具方法
//常用方法
//绝对值
abs(int a);
abs(double a);
//向上取整
ceil(double a);
//向下取整
floor(double a);
//四舍五入
round(float a);
//较大值,较小值
max(int a,int b);
min(int a,int b);
//返回a的b次幂
pow(double a ,double b);
//返回[0.0,1.0)间的随机值
random();
Object-对象类
是所有类的父类,所有类都可以调用Object类提供的方法
//常见方法
//返回对象的字符串形式
toString();"主要用于让子类重写调用"
//判断是否相等
equals(Object o);
//对象克隆(浅拷贝)
clone()
补充:浅拷贝和深拷贝的区别
- 浅拷贝:新对象与原对象数据一模一样,引用类型拷贝的只是地址
- 深拷贝:基本类型直接拷贝,字符串拷贝地址,其他对象会直接创建新对象
Objects-工具类
Objects是一个工具类,其中包含的都是静态方法,需要直接用类名调用
//常用方法
//判断是否相等
equals(Object a,Object b);
//判断是否为空
isNull(Object o);
Period-时期(年-日)
时期类,用于计算两个LocalDate对象间相差的年数,月数,天数
//1.构造方法
between(LocalDate start,LocalDate end);
//2.常用方法
//计算间隔
getYears();
getMonths();
getDays();
Random-随机
用于生成一个(伪)随机数,基础示例如下:
import java.util.Random;
public class hello {
public static void main(String[] args) {
Random rand = new Random();
int number = rand.nextInt(100);//随机数范围为0-99
System.out.println("生产随机数为:"+number);
}
}
详细介绍:
//1.构造方法
Random(long seed);//种子可省略
//2.常用方法
//整数
nextInt(int bound);//生成一个0到bound(不包括)间的整数,bound省略代表随机数范围为int范围
//浮点数
nextDouble();//范围为0.0-1.0
//布尔值
nextBoolean();
Runtime-运行环境
是一个单例类,是程序所在的运行环境
//1.构造方法
Runtime r = Runtime.getRuntime();
//2.常用方法
//终止虚拟机
exit(0);
//获取虚拟机能使用的处理器数
availableProcessors();
//返回java虚拟机的内存总量
totalMemory();
//返回java虚拟机的可用内存量
freeMemory();
//启动某个程序,返回代表这个程序的对象
exec(String command);//参数是程序的地址
Scanner-键盘输入
作用为获取键盘的输入,以下是一个基础示例:
import java.util.Scanner;//导入包
public class hello{
public static void main(String[] args){
Scanner sc = new Scanner(System.in);
int age = sc.nextInt();
System.out.println("你的年龄为:"+age);
}
}
详细用法介绍:
//1.基本构造方法
//从键盘读取
Scanner sc = new Scanner(System.in);//System.in是一个标准输入流,通常指键盘输入
//从文件读取
File fl = new File("input.txt");
Scanner sc = new Scanner(fl);
//从字符串读取
String str = "Hello world";
Scanner sc = new Scanner(str);
//2.读取不同数据
//整数
int number = sc.nextInt();
//浮点数
double number = sc.nextDouble();
//布尔值
boolean bool = sc.nextBoolean();
//字符串
//单个字符
String word = sc.next();
//整行文本
String str = sc.nextLine();
//3.检查输入
主要有hasNext(),hasNextInt()等,通常用于读取数据前,检查是否有下一个特定类型的数据
String-字符串
字符串类,不需要额外导入模块,注意首字母大写,下面为介绍:
//1.创建字符串
//直接双引号创建
String str = "Hello world";
//这种创建方法在创建相同字符串时,会直接返回其引用(也就是说相同内容字符串只会存储一份)
//String类构造方法创建
String str = new String("Hello world");"里面的内容也可以是字符数组"
//这种创建方法每次都会在堆内存创建一个新的字符串对象,并返回引用,注意,字符串返回对象均为引用,属于引用数据类型
//2.字符串方法
//获取字符串信息
//返回长度
length();
//返回index位置的字符
charAt(int index);
//返回子串,第二个参数可省略
substring(int beginIndex,int endIndex);
//字符串比较
str1.equals(str2);
//字符串修改
str2 = str1.replace('char1','char2');
//字符串查找
indexOf(int ch, int fromIndex);//fromIndex为查找的起始索引,可以省略
//字符串转数组
char[] a = str.toCharArray();
//判空
isEmpty();
//判断是否包含指定序列
contains(s);
//解析字符串
split(',');
//3.注意事项
1.String的改变每次都会在堆内存创建一个新的字符串对象,并返回引用,而其原来的对象不会改变,所有说String的对象是不可变的字符串对象。
StringBuilder-可变字符串
可变字符串对象,本质是容器,相比String更适合对字符串进行操作,运算效率更高
//构造方法
StringBuilder();
StringBuilder(int capacity);//指定容量初始化
StringBuilder(String str);//以指定字符串初始化
//常见方法
//添加数据
append(Object o);
"支持链式编程"
s.append(1).append("hi");
//反转字符串
reverse();
//返回长度
length();
//转换为String类型
toString();
StringBuffer-线程安全·可变字符串
方法和StringBuilder基本一样,但是StringBuffer是线程安全的,而StringBuilder线程不安全
StringJoiner-间隔符构造字符串
也是用于操作字符串的动态容器,效率同样很高,在某些场景下代码更简洁
//1.构造方法
StringJoiner(String separate);//指定拼接的间隔符
StringJoiner(String separate,String beginer,String ender);//指定拼接的间隔符,开始符,结尾符
//2.常用方法
//添加数据
add();
//返回长度
length();
//返回字符串
toString();
System-虚拟机系统
也是一个静态类,主要用于控制当前运行程序的java虚拟机
//1.常用方法
//关闭虚拟机
System.exit(0);//参数用作状态码,0表示人为中止,非零表示异常
//获取当前系统时间戳(1970-1-1 0:0:0到目前走过的总毫秒数,long类型)
System.currentTimeMillis();
//2.运用场景
//用于获取程序运行的时间
long beginTime = System.currentTimeMillis();
for(int i;i<10000;i++){
sout(i);
}
long endTime = System.currentTimeMillis();
sout((beginTime-endTime)/1000.0);
SimpleDateFormat-旧·时间解析串
用于对Date返回的时间进行格式化
//1.构造方法
SimpleDateFormat(String pattern);//pattern是专用时间格式,具体查看api文档,常用的有Y(年),M(月),d(日),H(时),m(分),s(秒)
//2.常用方法
//根据日期或时间戳格式化时间
format(Date date);
format(Object time);
//把字符串时间解析成日期对象
parse(String source);
Vector-容器
容器,动态可变数组,详细介绍如下:
//1.基本使用
//导入Vector类
import java.util.Vector;
//创建Vector对象
Vector<Integer> vec = new Vector<Integer>();
//2.常见方法
//插入元素
add(int index,obj);//省略index则在最后插入
//获取元素
get(int index);
firstElement();
lastElement();
//修改元素
set(int index,obj);
//删除元素
remove(obj);//只删除第一个匹配项
remove(int index);
removeAllElements();
//查询元素
indexOf(obj);//返回首次出现的索引
//获取大小
size();
ZoneId-时区
用于获取系统时区
//1.构造方法
systemDefault();
//2.常见方法
//获取ZoneId对象的时区id
getId();
//获取java支持的所有时区id
getAvailableZoneIds();
//把时区id封装成ZoneId对象
of(String id);
ZonedDateTime-时区时间
带时区的时间,用于替换Calendar
//1.构造方法
now();
now(ZoneId zone);//指定时区
//2.常见方法
//获取时间
getXXX();
//修改时间
withXXX(int val);
//增加减少时间
plus/minusXXX(int val);
设计模式
设计模式是指对于某一类问题的最优解法,共有20多种,学习设计模式,主要学习其解决什么问题和如何解决问题。
单例设计模式
单例设计模式需解决的问题是确保一个类只有一个对象。一个单例设计模式的写法主要有三步:
public class A{
//1.私有类构造器
private A(){}
//2.定义一个类变量对象
private static A a = new A();
//3.定义一个类方法返回类对象
public static A getObject(){
return a;
}
}
//经过这样封装后,从外界创建A类的对象就不能使用构造器,而只能使用类提供的方法,而类提供的方法返回的每次都是类变量,故一个类只能产生一个对象
//上述设计模式又称为饿汉式单例:即在拿对象之前,对象已经在类中被创建好了
//懒汉式单例
public class B{
//1.私有类构造器
private B(){}
//2.定义一个类变量用于存储对象
private static B b;
//3.提供一个类方法,保证返回的都是同一个对象
public static B getObject(){
if(b == NULL)
{
b = new B();
}
return b;
}
}
模板方法设计模式
模板方法设计模式的主要目的是为了解决方法中出现重复代码的问题,主要分三步:
//1.定义抽象类
public abstract class People{
//2.在类中定义模板方法和抽象方法
public void temple(){
sout("hello,my name is");
sout(myname());
sout("nice to meet you");
}
public abstract String myname();//定义抽象方法
}
//3.在子类实现抽象方法
public class Student extends People{
@override
public String myname(){
return "XiaoHong";
}
}
//实现时,只需要创建子类对象,调用父类的temple方法,就能成功运行
//推荐将模板类用final修饰,以防模板被篡改
public final void temple(){}