boolean(1bit) char (1字节) byte(1) short(2) int (4) long (8) float(4) double(8)
long 类型赋初值要在后面加l或L,float赋初值在后面加f或F,float可以表示的值比long大,因为一部分空间存储的是10的幂次。例E=10^。
自动类型转换
只能从小到大转换char 、byte 、short > int > long > float > double //自动转换
String、数组、接口、类、枚举、注解
String可以与基本数据类型进行+操作,但效果是连接。例:
“hello”+3+4.0 = =“hello34.0”
3+4.0+“hello” == ”7.0hello“ //按照优先级先算3+4.0等于7.0,再与“hello”连接
规则
1)、名称只能由字母、数字、下划线、$符号组成
2)、命名时不能以数字开头
3)、在命名是绝对不能出现Java关键字。
4)、绝对不允许在命名时出现中文命名。
规范
1)、包名全小写。例:xxxyyyzzz
2)、类名和接口名首字母大写。例:XxxYyyZzz
3)、方法名和属性名第一个单词小写,后面的首字母大写。例:xxxYyyZzz
4)、常量全部大写并且每个单词之间用下划线分割。例:XXX_YYY_ZZZ
import java.util.Scanner; //导包
Scanner sc=new Scanner(System.in); //创建对象,sc可以变
int num=sc.nextInt(); //输入int型 ;//其他8中数据类型同上:nextXxx
sc.next(); //string
//(Scanner没有为char提供相关方法,char可以使用string获取,或者如下)
String a = sc.next();
char b = a.charAt(0); //()内放下标,获取字符串中的某个字符
表达式只能是byte、short、char、int、枚举类型、String这六种数据类型
switch(表达式){
case 常量1:
执行语句1;
break;
case 常量2:
执行语句2;
break;
.
.
.
default:
执行语句n;
break;
}
//输出为
i:1
label:for(int i=1;i<=5;i++){
System.out.print("\ni:"+i+" ");
for(int j=1;j<=5;j++){
if{j==i}
break label; //结束指定标识的那层循环结构
System.out.print(j);
}
}
//输出为
i:1
i:2 1
i:3 12
i:4 123
i:5 1234
label:for(int i=1;i<=5;i++){
System.out.print("\ni:"+i+" ");
for(int j=1;j<=5;j++){
if{j==i}
continue label; //跳过指定标识的那层循环结构
System.out.print(j);
}
}
数组元素默认初始化值:
int : 0
double :0.0
char :0 而非 ‘0’
boolean:false
string :null
数组创建与C++不同
如何遍历:
a [ i ] ,i为索引 0<= i <a的容量
一般使用for循环来遍历每一个数组元素的值
获取数组长度:
a.length //数组名.length
数组存放:
局部变量(方法内声明的变量)存放在栈区,new出来的空间存放在堆区。例:
二维数组
//静态初始化
String[][] a1=new String[][]={ {"ll","gf"},
{"hi","jo"},
{"fk","fk"},
{"fl","dk"}};
//动态初始化1
String[][] a2=new String[n][m];
//动态初始化2
String[][] a3=new String[n][]; //可以不写后面的大小,不能不写前面的大小
a3[0]=new String[5]; //为a3[0]开辟一个大小为5的空间,否则访问a3[0][0]会空指针异常
内存结构:
例:
int[][] a=new int[4][3];
double[][] b=new double[4][];
b[0]; //结果为null,因为第二维还没有开辟空间。
数组长度:
int[][] a=new a[][] { {1,2,3}, {4,5}, {6,7,8} }; //第二维空间可以不一样长
a.length; //结果为3
a[0].length; //结果为3
a[1].length; //结果为2
和c++中的深浅拷贝问题相似
//错误示范
int[] a,b;
a=new int[]{1,2,3,4};
//正确操作
int[] a,b;
a=new iint[]{1,2,3,4};
b=new int[a.length]; //开辟新空间并赋值,这样修改数组b不会影响数组a
for(int i=0;i<a.length;i++)
b[i]=a[i];
1、boolean equals(int a [] , int b[]); //判断两个数组是否相等
2、String toString(int[ ] a); //输出数组信息
3、void fill(int[ ] a,int val); //将数组中的元素值变为val
4、void sort(int[ ] a); //数组快排
5、int binarySearch(int[ ] a,int key); //二分查找,返回查找到元素的索引值
堆:存放实例化的对象(属性)
栈:存储局部变量
方法区:存储类信息、常量、静态变量
如图:创建对象时的内存如下(图片来自尚硅谷培训课)
Student[] s=new Student[20];
for(int i=0;i<20;i++){
//必须为每一个对象开辟新空间,否则会空指针异常
s[i]=new Student();
s[i].number=i+1;
s[i].score=(int) (Math.random()*100);
}
分配内存如下:
栈 堆
特征:只能调用一次
使用:
new 类名() . 方法(); //匿名对象1
new 类名() . 属性; //匿名对象2
jdk5.0增加的内容
格式: 数据类型 ... 形参名 ,在方法形参的声明中必须放在末尾且只能存在一个
show("hello","word"); //可以传入0个或多个实参,都可调用show(String ... s)
public void show(String ... s){for(int i=0;i<s.length;i++){
System.out.print( s[ i ] ); //可以通过下标来访问,和数组一样}
}
public void show(String s){
//构成重载
}
public void show(String[] s){
//重载失败,和 show( String ... s) 重复
}
public void show(String ... s,int a){
//编译错误,String ... s 必须放在最后声明
}
public void show(String ... s,int ... a){
//编译错误,可变个数形参只能存在一个
}
例:
修饰类的话只能使用缺省或public
作用:
1、创建对象。例:new 构造器
2、初始化对象的属性。
如果没有定义构造器,系统提供一个默认空参数构造器,构造器不是方法,不需要用对象来调用
格式:权限修饰符 类名( 形参 ){ } 注:构造器不要写返回类型 传入形参可以在构造对象时初始化该对象的属性。
例:
public Person(String n){
name=n;
}
构造器可以定义多个形成重载,一个类中起码有一个构造器。
注:如果自己定义了构造器,则系统不会提供默认的空构造器
使用this关键字。this代表当前对象。
this可修饰属性、方法和构造器。一般省略this,除非形参与属性同名,为了区分使用this.属性。
格式:this(形参) 注:必须放在构造器首行,调用不可以形成环(死循环) ,一个构造器内部最多只能调用一次其他的构造器
通过形参唯一的对应你想调用的构造器。例:
class Person{
private int age;
private int name;
public Person(){
this(12); //调用下面的构造器Person(int age)
}
public Person(int age){
this.age=age;
}
public Person(int age,String name){
this.age=age;
this.name=name
}
}
是一种可重用组件,符合下面标准:
1、类是公共的
2、有一个无参的公共构造器
3、有属性,且有对应的get、set方法
关键字:extends
格式:
public class 子类名 extends 父类名
c++允许类继承多个父类,Java不允许继承多个父类
私有属性可以继承,但不能直接访问。
Object类是所有类的父类(java规定的)
子类重写父类方法,方法名和形参必须相同。重写后子类对象(直接或间接)访问该方法访问的是子类中重写的方法(子类重写后的方法将父类中被重写的方法覆盖了)。
规定:1、方法名和形参必须相同
2、子类重写方法的权限修饰符不能比父类的小,特殊情况:子类不能重写父类中的私有方法(如果写了不会报错,但这不是重写,而是属于子类的方法)
3、非static的方法可以重写,static方法的同名同参方法不是重写,因为静态方法不可以被覆盖。
4、返回值类型
(1) 父类被重写方法返回值类型是void,子类重写方法的返回值类型只能是void
(2)父类被重写方法返回值类型是A(引用数据类型),子类重写方法的返回值类型是A或A的子类:例,Object是String父类。
(3) 父类被重写方法返回值类型是基础数据类型,子类重写方法的返回值类型必须相同。
(4)子类重写方法抛出的异常的类型不大于父类被重写方法抛出的异常类型(具体看异常处理部分)
适用于区分子父类的同名方法、属性和构造器,使用super表示调用的是父类中的。
1、super可以理解为:父类的
2、super可以调用属性、方法、构造器。使用方式:super.属性、super.方法()
特别的:super调用父类的构造器的格式为super(形参),通过形参唯一的确定调用的是父类中指定构造器。
注意:
1、super必须写在子类构造器的首行。(this(形参列表)构造器调用构造器也必须在构造器首行,所以二者只能存在一个)
2、如果在构造器中this(形参)或super(形参)都没有被显式的声明,则构造器首行默认声明了一个super(),即父类中的空参构造器。也就是说当出现上述情况的时候,创建一个子类对象,子类构造器会先调用父类的空参构造器,然后再运行子类构造器的方法体。
3、当父类中没有空参构造器,子类的构造器中也并不显式的声明this(形参)或super(形参),则子类构造器报错,因为构造器首行提供的隐式super()调用父类的空参构造器,但父类没有空参构造器。
4、在类的n个构造器中至少有一个构造器使用了super(形参),因为this(形参)最多只能有n-1个(否则就会构成环)。所以在实例化子类对象的时候,一定会调用父类的构造器。
格式:父类 对象名=new 子类();
使用规则:
1、多态是值事物的多种形态。
2、父类的调用指向子类的对象(子类的对象赋给父类的调用)
3、使用多态的前提:继承关系、重写方法
4、虚拟方法调用:编译时,只能调用父类中声明的方法,但在运行期,实际执行的是子类中重写的方法。
5、对象的多态性只适用于方法,不适用于属性。(属性不存在重写)
强制转换又叫 向下转型:
1、强制转换前和转换后的类型必须是父子关系(直接或间接)
2、强制转化后的类型不能小于new出来的类型
3、强制转化后的类型不能大于等式左侧的类型
例:
Man和Woman是Person的子类
Person p=new Man();
Man m=(Man) p; //将p强制转换为Man类型,编译成功,运行成功
Woman w =(Woman) p; //将p强制转换为Woman类型,编译成功,运行不成功(ClassCastException错误)
强制转换可能会出错,所以使用instanceof关键字来规避这种可能出现的错误
使用方法:
a instanceof A 如果a是类A的实例化,返回true,表示a可以强制转换为A类型
例:
Person p= new Man();
if(p instanceof Man){//如果可以强制转化,则强制转化
Man m=(Man) p;
}
1.选中当前工程-右健选择, build path - add libraries - JUnit 4 -下一步
2.创建Java类,进行单元测试,
此时的Java类要求:此类是public的、此类提供公共的无参的构造器
3.此类中声明单元测试方法。
此时的单元测试方法,方法的权限是public,没有返回值,没有形参
4.此单元测试方法上需要声明注解: @Test, 并在单元测试类中导入,import org.junit.Test;
5.声明好单元测试方法以后,就可以在方法体内测试相关的代码。
6.写完代码以后,左键双击单元测试方法名,右键。run as -- JUnit Test
说明:
1.如果执行结果投有任何异常,绿条
2、出现异常,红条。
测试方法作用与main方法相同,但不同的是main方式是静态的,如果要调用该类中的其他方法或属性,需要通过对象来调用。而测试方法不是静态的,可以直接调用
例如:
package test;
import org.junit.Test;
public class JUnitTest {
int h=10;
@Test
public void testEquals() {
System.out.println(h);
}
}
1、Object类是所有类的根父类
2、如果在类的生命中未使用extends关键字知名父类,则默认父类是Java.lang.Object类
获取父类的方法:对象名.getClass().getSuperClass();
3、Object类的功能(属性、方法)具有通用性
4、Object类之声明了一个空参构造器
作用:克隆,复制一个新的对象
格式:对象名.clone();
返回值类型是Object类,所以接受时需要强转类型
例:
Animal a1=new Animal("花花");
Animal a2=(Animal) a1.clone();
finalize方法
对象被回收之前,会调用该方法。(不要自己调用,垃圾回收器会自己调用)
==和equals方法的区别
==:可以比较基本数据类型和引用数据类型
1、比较的是基本数据类型的话,比较的是两个变量保存的数据是否相等
equals()方法:只能比较引用数据类型
String类、Date类、File类、包装类等都重写了equals()方法,将该方法变为了比较两个对象的数据值是否相同
使用格式:
对象名1.equals(对象名2); //相同返回true,不同返回false
重写Object类中的 equals():为了让自定义的数据类型也能比较数据值
手动实现:
public class Student {
private int age;
private String name;
public Student(int age, String name) { //构造器
super();
this.age = age;
this.name = name;
}
public boolean equals(Object obj) {
if (this == obj) { // 如果地址相同,数据肯定相同
return true;
}
if (obj instanceof Student) { // 如果对象2是该类的对象
Student stu = (Student) obj; // 强制转换为该类对象
return this.age == stu.age && this.name.equals(stu.name); // 比较所有的属性是否相等
}
return false;
}
public static void main(String[] args) { //测试
Student s1 = new Student(12, "Tom");
Student s2 = new Student(12, "Tom");
System.out.println(s1.equals(s2)); //结果为true
}
}
软件可以自动生成equals:(eclipse软件快捷键为Ctrl+Alt+s,生成hashcode和equals项)
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
Student other = (Student) obj;
if (age != other.age)
return false;
if (name == null) {
if (other.name != null)
return false;
} else if (!name.equals(other.name))
return false;
return true;
}
当我们输出一个对象的引用时,也就是调用了toString()方法。
Person a=new Person();
System.out.println(a); 相当于 System.out.println(a.toString());
String、Date、包装类、File等类都重写了toString方法,使该方法的作用是输出数据值。
重写toString方法:
手动:输出属性值即可
public String toString() {
return "Student[name = " + name + ", age = " + age + "]";
}
自动:eclipse软件快捷键为Ctrl+Alt+s,生成toString方法
@Override
public String toString() {
return "Student [age=" + age + ", name=" + name + "]";
}
包装类是什么:针对八种基本类型定义了相应的引用类型---包装类
意义:基本数据类型有了类的特点就可以调用类的方法,Java才是真的面向对象
注:有了类之后就可以调用类中的方法了,也就是可以使用Object中的equals()等方法了
使用:
基本数据类型转换为对应包装类:调用包装类的构造器
// 方法一
int num = 10;
Integer i1 = new Integer(num); //Integer(value)格式,也可以直接写10
// 方法二
Integer i2 = new Integer("10"); //Integer(String)格式
System.out.println(i1.toString()); //10
System.out.println(i2.toString()); //10
通过方法转换
Integer in1=new Integer(12);
int i1=in1.intValue(); //通过方法转换
Float fl1=new Float(12.0);
float f1=fl1.floatValue(); //通过方法转换
自动装箱: //不需要调用构造器转换
Integer in1=num1;
Integer in2 =10;
自动拆箱: //不需要调用方法转换
int num2=in1;
基本数据类型、包装类 -----> String类
方式一:连接运算
int n=10;
String s=n+"";
方式二:通过valueof方法 括号内可以放包装类和基本数据类型
float f=12.3f;
String s1=String.valueof(f);
Double d=new Double(12.4);
String s2=String.valueof(d);
String类 ----->基本数据类型、包装类
调用包装类的parseXxx()方法。
String s="123";
int n=Integer.parseInt(s);
Integer n2=Integer.parseInt(s);
当然,如果String是abc之类的就不能转换成int、double等不对应类型
修饰属性放在数据类型前,修饰方法放在返回类型前。
属性
1、静态的、可以修饰方法、属性、代码块、内部类。
2、多个对象共享静态属性,当一个对象修改了属性,其他对象再使用的就是被修改后的属性
3、静态属性在类加载的时候加载,早于对象的创建,随着类的消亡而消亡。
4、类只加载一次,所以静态变量在内存中只存在一份,存在方法区的静态域中。
5、静态变量可以直接通过类.属性的方式进行调用。
方法
1、静态方法中不可以使用this、super关键字。
2、静态方法中通过(类.静态属性/方法)的形式来调用静态属性/方法(类内间的调用可以省略类.),通过对象来调用非静态方法/属性。非静态方法中调用其他属性或方法不需要考虑这些。
3、静态方法调用几次就会执行几次。
什么时候设置static?
① 共享属性可以修饰为静态,例如银行利率。
② 工具类的方法可以声明为静态,这样就可以直接类.方法()调用。例:Math.PI
③ 操作静态属性的方法可以设置为静态。
某个类只能存在一个对象
如何实现?
1、构造器权限设置为private
2、在类内创建对象
3、创建static方法返回这个唯一的对象
4、static方法返回的只能是静态的对象,所以对象修改为static类型
饿汉单例设计模式:
声明对象时直接new一个对象,缺点:对象加载时间过长
懒汉单例设计模式:
只声明对象,在使用返回对象方法时再创建对象。
应用场景:
1、网站计数器
2、数据库连接池
格式:
{
代码
}
作用:用来初始化类、对象。
代码块如果有修饰的话,只能使用static
静态代码块:随着类的加载只执行一次,初始化类的信息。
非静态代码块:随着每一个对象的加载而执行,初始化对象。
使用情景:某些操作只需要执行一次,就将代码放入静态代码块中。
静态代码块是在类加载时最早执行的。
注:由父及子,静态代码块最先执行。当父子类的所有静态代码块都执行后,再执行代码块和构造器。先执行代码块,在执行构造器,一个类一个类的执行。
属性赋值:代码块中赋值语句执行次序和显式初始化相同,看谁在前面就先执行谁,比构造器中赋值语句执行快。
可以修饰的结构:属性、方法、类。放在权限修饰符后
1、修饰一个类,代表这个类不会有子类了(不可以被继承)。格式:final class 类名
例:String类
2、修饰一个方法,代表方法不能被重写。
3、修饰变量,代表变为了“常量”
3.1、修饰属性
①、显式初始化。例:final int A=10;(常量名全部大写)
②、在方法体内初始化
③、在构造器中初始化,如果有多个构造器,每个构造器都需要为其初始化。好处:每个对象可以有不同的常量值。
3.2、修饰局部变量
①、显式初始化
②、作为形参,调用方法时初始化
static final 可以修饰的结构:属性(变为全局常量)、方法
可以修饰的结构:类、方法。放在权限修饰符后。
一、修饰类:抽象类
1、特点:不可以实例化
2、抽象类中一定有构造器,便于子类实例化调用。
3、开发中都会提供抽象类的子类,让子类进行实例化,如果没有子类那么抽象类将没有意义。
二、抽象方法
1、只有声明,没有方法体。
2、包含抽象方法的类也必须是抽象类
3、若子类重写了父类中所有抽象方法后才可以实例化,否则意味着子类也是抽象类,必须使用abstract修饰。
4、不能修饰私有方法,静态方法、final的方法(三者不能被重写)。
三、创建抽象类的匿名子类对象
匿名对象创建:Person 类是Man类父类
new Man();
匿名子类的对象创建:
Person p =new Person(){
//使用Person来代表子类,不是创建了Person类的对象,毕竟方法体内重写了抽象方法
重写下Person类中的抽象方法
};
匿名子类的匿名对象创建:
new Person(){
//使用Person来代表子类,不是创建了Person类的对象,毕竟方法体内重写了抽象方法
重写下Person类中的抽象方法
};
当作为形参传入method(Person p)方法体内时,代码:
method(new Person() {
重写父类抽象方法
});
也就是在method(); 方法体内传入形参的地方写匿名子类的匿名对象创建代码。
模板方法设计模式
模板很通用,但有一部分易变,就将这部分声明为抽象,由子类来重写这部分,
比如:数据库访问的封装
1、接口和类是并列的结构
2、接口可以定义什么成员:
①、JDK7及以前只可以定义全局变量和抽象方法
全局变量 :public static final ,通常这些修饰符可以省略,但意思依旧是全局常量
抽象方法:public abstract ,通常这些修饰符可以省略。抽象方法只能声明,不能有方法体
②、JDK8及以后,可以有静态方法和默认方法
3、接口没有构造器,意味着不能实例化(抽象类不能实例化,但必须有构造器)
4、接口必须通过类来实现(implements)格式:class 类名 implements 接口名
实现类必须重写接口类中的方法,否则该类是抽象类。
5、类可以实现很多个接口,弥补了只能单继承的缺陷。
格式:class 类名 implements 接口1 , 接口2 (用,隔开)
6、当类又要实现接口,又要继承父类的时候,先写继承后写实现。
7、接口间可以继承,可以多继承。
格式:interface 接口1 entends 接口1,接口2 (用,隔开)
8、接口的具体使用体现了多态性(声明的是接口,但实际上创建的是实现类对象)
例:
public class InterfaceTest {
public static void main(String[] args) {
Computer c = new Computer();
Flash f = new Flash(); //创建实现类对象
c.transferData(f); //运行接口方法
}
}
class Flash implements USB { //实现类
public void start() {
System.out.println("Flash 开始启动");
}
public void end() {
System.out.println("Flash 关闭");
}
}
class Computer {
public void transferData(USB usb) { //使用多态,USB usb=new Flash();
usb.start();
usb.end();
}
}
interface USB { //接口
void start();
void end();
}
9、接口实际上可以看作是一种规范
10、四种对象类型创建(匿名对象非匿名对象匿名子类非匿名子类)
package day101;
public class InterfaceTest {
public static void main(String[] args) {
Computer c = new Computer();
// 1、创建了实现类的非匿名子类的非匿名对象
Flash f = new Flash(); // 创建实现类对象
c.transferData(f); // 运行接口方法
// 2、创建了实现类的非匿名子类的匿名对象
c.transferData(new Flash());
// 3、创建了实现类的匿名子类的非匿名对象
USB usb = new USB() {
public void start() {
}
public void end() {
}
};
// 4、创建了实现类的匿名子类的匿名对象
c.transferData(new USB() {
public void start() {
}
public void end() {
}
});
}
}
class Flash implements USB {
public void start() {
System.out.println("Flash 开始启动");
}
public void end() {
System.out.println("Flash 关闭");
}
}
class Computer {
public void transferData(USB usb) {
usb.start();
usb.end();
}
}
interface USB {
void start();
void end();
}
同:
① 都不能实例化
② 都可以被继承
异:
① 抽象中有构造器,接口没有
② 抽象类中单继承、接口多继承
package day101;
public class InterfaceTest {
public static void main(String[] args) {
Agent agent=new Agent(new RealStar());
agent.sing();
agent.money();
}
}
//被代理类
class RealStar implements Star {
public void sing() {
System.out.println("歌手在唱歌");
}
public void money() {
}
}
//代理类
class Agent implements Star {
private Star star; //利用多态性,创建一个私有引用
public Agent(Star star) { //构造器为引用赋一个具体的对象
super();
this.star = star;
}
public void sing() {
star.sing(); //让具体的被代理对象去唱歌
}
public void money() {
System.out.println("经纪人在谈钱");
}
}
interface Star {
void sing();
void money();
}
工厂设计模式
工厂方法模式
抽象工厂模式
接口中可以定义静态方法和默认方法(有方法体的方法)
interface CompareA {
//静态方法
public static void method() {
System.out.println("hhh");
}
//默认方法
public default void method2() {
System.out.println("上海");
}
}
1、实现类可以不重写静态方法和默认方法
2、接口中的静态方法只能接口来调用,实现类只能调用默认方法
3、实现类重写默认方法时需要将default 删掉
4、子类(实现类)继承的父类和实现的接口中声明了同名同参的默认方法,那么子类在没有重写此方法的情况下,调用的是父类中的该方法-----方法调用时类优先原则(属性必须严格区分是父类的还是接口的)
5、如果实现类实现了多个接口,多个接口中定义了同名同参的默认方法,在实现类没有重写此方法的情况下,编译器报错------接口冲突,必须重写此方法
6、重写了接口中的方法,又想要调用接口中的原方法,那么采用----- 接口.super.方法()
jdk9中允许接口中有私有方法
1、java允许类A声明在类B中,类A是内部类,类B是外部类
2、内部类的分类:成员内部类(静态、非静态),局部内部类(方法内、代码块内、构造器内)
3、成员内部类:
① 外部类的成员
调用外部类的结构:外部类.this.结构
可以被static修饰
可以被四种权限修饰符修饰
② 作为一个类
类内可以定义属性、方法、构造器等
可以用abstract修饰,不可以实例化
可以用final 修饰,不可以被继承
4、关注如下三个问题
① 如何实例化成员内部类的对象
静态的成员内部类://静态结构可以通过类名.结构调用
外部类.内部类 对象名 = new 外部类.内部类();
非静态的成员内部类://非静态结构可以通过对象名.结构调用
外部类 对象名1 = new 外部类();
外部类.内部类 对象名2 = 对象名1.new 内部类();
② 如何在成员内部类中调用外部类结构
外部类.this.结构 (如果没有重名结构,可以直接使用结构名来调用)
③ 开发中局部内部类的使用
注意点:局部内部类中如果调用了外部方法(方法内写着该内部类)的属性,要求此属性是final类型,JDK8及以后可以省略final
1、final类型,不可被继承
2、String实现了Serializable接口,表示字符串是支持序列化的
实现了Comparable接口,表示String可以比较大小
3、String内部定义了final char[] value用于存储字符串数据
4、String:代表了不可变的字符序列。简称不可变性
5、通过字面量的方式(区别于new)给一个字符串赋值,此时字符串值声明在字符串常量池中
6、 字符串常量池中不会存储相同内容的字符串的
String s1="abc"; //字面量的定义方式
内存如下:
常见构造器类型:
字面量与构造器赋值的内存存储有何区别:
注意:
1、 String s=new String("abc");
在内存中创建了两个对象,一个是堆空间中的属性,一个是字符串常量池中的“abc”
2、常量与常量的连接结果在常量连接池,但只要其中有一个变量,那么会在堆空间开辟一个空 间,注意,finial修饰的时常量。
例:
String s1="A";
String s2=s1+"B"; //s2在堆中开辟一个空间,在常量池中创建一个“AB”字符串,s2指向堆空 间,堆空间指向常量池。
6、字符串数组:String[] s=new String[5];或String[] s=new String[]{"","","","",""};或String[] s={"","","","",""};
String常用的方法
String s = new String("HellorWord");
System.out.println(s.contains("Hello")); //测试是否含有子串:true
String s2="Word";
System.out.println(s.contains(s2));//测试是否含有子串:true
System.out.println(s.lastIndexOf("or")); //指定字符串最后一次出现的索引:7
System.out.println(s.lastIndexOf("or",7)); //指定索引为7从右向左查找指定字符串最后一次出现的索引:7
String 与 包装类、基本数据类型的转换
//String -> 包装类、基本数据类型通过Xxx.pareofXxx()方法
String s = new String("123");
int n=Integer.parseInt(s);
System.out.println(n); //123// 包装类、基本数据类型->String通过valueOf(xxx)方法
s=String.valueOf(n);
System.out.println(s); //123
String 与char[ ] 之间的转换
//String转换为char[]通过toCharArray方法
String s = new String("HelloWord");
char[] array = s.toCharArray();
for(int i=0;i<array.length;i++) {
System.out.println(array[i]);
}//char[]转换为String通过构造器
char[] a=new char[] {'t','e','s','t'};
String s1=new String(a);
System.out.println(s1);
String与byte[ ]的转换
编码:字符串-> 字节;
String -> byte[ ]:调用String的getBytes()
String str1="abc123中国";
byte[] bytes=str1.getBytes();
System.out.println(Arrays.toString(bytes));
//[97, 98, 99, 49, 50, 51, -28, -72, -83, -27, -101, -67] //utf-8中文分3个数字储存解码: 字节->字符串;
byte[ ] -> String:调用构造器
String str2=new String(bytes);
System.out.println(str2); //abc123中国
解码集与编码集不一致会导致乱码
JDK11新增的方法
isBlank():判断字符串是否是空白
strip():去掉首尾空格
stripTrailing() 去掉尾部空格
stripLeading() 去掉首部空格
repeat(int count) 复制字符串
lines().count() 行数统计
String Buffer sb1=new StringBuffer() ; //char[] value=new char[16];底层创建了一个长度为16的字符数组。
sb1.length();//0,数组名.length 返回的是数组的容量,字符串.length()返回的是字符串的长度,后者的length加();
sb1.append('a'); value[0]=‘a';
如何扩容?
如果数据底层数据盛放不了,那就需要扩容底层数组,默认情况下将扩容为原来的2倍+2 ,同时原有数组赋值到新数组中。
建议使用可以设置底层数组容量的构造器,避免扩容。
StringBuilder与StringBuffer底层相似。
StringBuffer\StringBuilder常用方法:
//delete删除 [start,end) 区间的字符,左闭右开
StringBuffer sb1 = new StringBuffer("abcd");
sb1.delete(0, 1);
System.out.println(sb1); //bcd//indexOf:返回指定字符串第一次出现位置的索引值
StringBuffer sb1 = new StringBuffer("abcdcd");
System.out.println(sb1.indexOf("cd")); //2//substring:返回 [start,end)区间的字符串
StringBuffer sb1 = new StringBuffer("abcdcd");
System.out.println(sb1.substring(2, 4)); //cd
StringBuffer\StringBuilder中不能使用+进行字符串连接
StringBuffer sb1 ="aaaa"; //错误,String类型不能赋值给StringBuffe、StringBuilderr类型
对比String、StringBuffer、StringBuilder速度。
从高到低:StringBuilder、StringBuffer、String
String、 StringBuffer、StringBuilder异同:
String:不可变的字符序列、底层结构使用char[]存储
StringBuffer:可变的字符序列、线程安全的、效率低、底层结构使用char[]存储
StringBuilder:可变的字符序列、jdk5新增,线程不安全、效率高、底层结构使用char[]存储
System类中的currentTimeMillis()
//返回当前时间与1970年1月1日0时0分0秒之间以毫秒为单位的时间差,称为时间戳
long time=System.currentTimeMillis(); //输出为1633409791462毫秒
Date存在两个类:java.util.Date类、java.sql.Data类。
其中java.util.Date类是java.sql.Data类的父类
1、两个构造器的使用:
①、Date():创建一个对应当前时间的Date对象
②、Date(long Date):创建了一个指定毫秒数的Date对象 //sql中的Date类只有这种构造器
2、两个方法的使用:
①、toString():显示当前年、月、日、时、分、秒、
②、getTime():显示当前Date对象的时间戳
3、java.sql.Data类对应着数据库中的日期类型的变量
4、java.util.Data类对象如何转为java.sql.Date类
(通过 getTime()方法将java.util.Date对象的时间戳作为参数传入java.sql.Date构造器中)
java.util.Date date1=new java.util.Date();
System.out.println(date1.toString()); //Tue Oct 05 16:01:49 CST 2021
System.out.println(date1.getTime()); //1633420909333
java.sql.Date date2=new java.sql.Date(date1.getTime());
System.out.println(date2); //2021-10-05
关注两个操作:
1、格式化 :日期-->字符串 使用SimpleDateFormat类中的 format(Date) 方法
2、解析 :格式化逆过程 使用SimpleDateFormat类中的 parse(str) 方法
.注:① str格式必须和类中设置的格式相同,否则会出异常。
② parse(str) 方法返回的是java.util.Date类型的,如果要转换为java.sql.Date类型需要调用构造器
//1、实例化SimpleDateFormat对象
SimpleDateFormat sdf=new SimpleDateFormat();
//2、格式化:
Date date=new Date();
System.out.println(date); //Tue Oct 05 19:55:31 CST 2021
String format=sdf.format(date);
System.out.println(format); //21-10-5 下午7:55
//3、解析
String str="21-12-2 下午10:23"; //必须和SimpleDateFormat的格式相同
Date date1=sdf.parse(str);
System.out.println(date1); //Thu Dec 02 22:23:00 CST 2021
/*可以通过构造器修改格式
* 常用格式:yyyy-MM-dd hh:mm:ss
*/
SimpleDateFormat sdf1=new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
System.out.println(sdf1.format(date));
//2021-10-05 08:06:29输出格式为设置格式
常用方法:
public static void main(String[] args) {
//1、实例化
//方式一:创建子类(GregorianCalendar)对象
//方式二:调用静态方法getInstance();
Calendar calendar=Calendar.getInstance();
//2、常用方法
//get()
int days=calendar.get(Calendar.DAY_OF_MONTH);
System.out.println(days); //5,这个月的第几天
System.out.println(calendar.get(Calendar.DAY_OF_YEAR)); //278,今年的第几天
//set()
calendar.set(Calendar.DAY_OF_MONTH, 10); //修改日期,日历本身改变(JDK8修改了这点)
days=calendar.get(Calendar.DAY_OF_MONTH);
System.out.println(days); //10,这个月的第几天被修改为10
//add
calendar.add(Calendar.DAY_OF_MONTH, 3);//这个月的第几天加上3
days=calendar.get(Calendar.DAY_OF_MONTH);
System.out.println(days); //13
//getTime(): 日历类 -> Date
System.out.println(calendar.getTime()); //Wed Oct 13 20:45:38 CST 2021
//setTime() Date->日历类
Date date=new Date();
calendar.setTime(date);
days=calendar.get(Calendar.DAY_OF_YEAR);
System.out.println(days); //278
}
注意:获取月份:一月是0.。。12月是11
获取星期:周日是1。。。周六是7
jdk8以前面临的问题:
1、可变性:Calendar日期可修改。
2、偏移性:年份从1900开始,月份从0开始,为了正确输入时间,年份-1900,月份-1。
3、格式化、格式化只对Date有用,Calendar不行。
4、它们也不是线程安全的,不能处理闰秒等。
LocalDate、LocalTime、LocalDateTime类,他们的实例化对象是不可变的对象(类似于Calendar)
LocalDate:IOS格式(yyyy-MM-dd)的日期
LocalTime:表示一个时间,而不是日期
LocalDateTime:表示日期和时间,这是最常用的类之一
常用方法代码示例:
public static void main(String[] args) {
//now:获取当前日期、时间、日期和时间
LocalDate localDate=LocalDate.now();
LocalTime localTime=LocalTime.now();
LocalDateTime localDateTime=LocalDateTime.now();
System.out.println(localDate); //2021-10-05
System.out.println(localTime); //21:19:48.153
System.out.println(localDateTime); //2021-10-05T21:19:48.153
//of():设置指定的年月日时分秒,没有偏移量
LocalDate localDate1=LocalDate.of(2020,3,7);
System.out.println(localDate1); //2020-03-07
//getXxx():获取当前年、月、日第几天
System.out.println(localDateTime.getDayOfMonth()); //这个月第几天 5
System.out.println(localDateTime.getDayOfYear());//今年第几天 278
System.out.println(localDateTime.getHour()); //小时 21
System.out.println(localDateTime.getMonthValue()); //月份 10
System.out.println(localDateTime.getYear()); //年 2021
//withXxx():设置相关属性,返回的副本被修改,对象本身没有变,体现了不可变性
System.out.println(localDate.withDayOfMonth(10)); // 2021-10-10
System.out.println(localDate); // 2021-10-05
//plusXxx :加多长时间,体现不可变性
System.out.println(localDate.plusDays(3));//加3天 2021-10-08
System.out.println(localDate);//2021-10-05
//miusXxx:减多长时间, 体现 不可变性
System.out.println(localDate.minusDays(2));//减2天 2021-10-03
System.out.println(localDate);//2021-10-05
}
使用方式:返回的时间都是本初子午线时间
public static void main(String[] args) {
//实例化方式一:now:获取本初子午线当前日期、时间、日期和时间
Instant instant=Instant.now();
System.out.println(instant); //2021-10-05T13:43:18.549Z,
//本初子午线时间,与本地区时间差8个小时
//ZoneOffset.ofHours:调整时间差
OffsetDateTime offsetDateTime=instant.atOffset(ZoneOffset.ofHours(8));
System.out.println(offsetDateTime);//2021-10-05T21:46:43.732+08:00
//toEpochMilli():获取距离1970年1月1日0时0分0秒(UTC)毫秒数
Long mill=instant.toEpochMilli();
System.out.println(mill); //1633441924380
//实例化方式二:ofEpochMilli(Long mills)
Instant instant1=Instant.ofEpochMilli(1633441924380L);
System.out.println(instant1); //2021-10-05T13:49:39.798Z
}
用来格式化或解析日期时间,类似于SimpleDateformat类
使用:
package day104;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.time.Instant;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.time.LocalTime;
import java.time.OffsetDateTime;
import java.time.ZoneOffset;
import java.time.format.DateTimeFormatter;
import java.time.format.FormatStyle;
import java.time.temporal.TemporalAccessor;
import java.util.Arrays;
import java.util.Calendar;
import java.util.Date;
public class Test {
public static void main(String[] args) {
// 实例化方式一:ISO_Local_Xxxx
DateTimeFormatter formatter1 = DateTimeFormatter.ISO_LOCAL_DATE_TIME;
// 格式化:日期->字符串
LocalDateTime localDateTime = LocalDateTime.now();
String str = formatter1.format(localDateTime);
System.out.println(str); // 2021-10-05T22:04:29.038
// 解析:字符串->日期
TemporalAccessor parse = formatter1.parse(str); // 格式必须相同
System.out.println(parse);// {},ISO resolved to 2021-10-05T22:15:53.213
// 实例化方式二:本地化相关格式:ofLocalizedDateTime
// 适用:FormatStyle.LONG / FormatStyle.MEDIUM / FormatStyle.SHORT
// 本地化相关格式:ofLocalizedDateTime
// 适用:FormatStyle.FULL / FormatStyle.LONG / FormatStyle.MEDIUM /
// FormatStyle.SHORT
DateTimeFormatter formatter2 = DateTimeFormatter.ofLocalizedDateTime(FormatStyle.LONG);
// 格式化
String str2 = formatter2.format(localDateTime);
System.out.println(str2); // 2021年10月5日 下午10时12分47秒
// 解析:字符串->日期
TemporalAccessor parse1 = formatter2.parse(str2); // 格式必须相同
System.out.println(parse1);// {},ISO resolved to 2021-10-05T22:15:53
// 重点:实例化方式三:自定义的格式。如:ofPattern("yyyy-MM-dd hh:mm:ss E")
DateTimeFormatter formatter3 = DateTimeFormatter.ofPattern("yyyy-MM-dd hh:mm:ss");
// 格式化
String str3 = formatter3.format(LocalDateTime.now());
System.out.println(str3); // 2021-10-05 10:27:53
// 解析
TemporalAccessor parse3 = formatter3.parse("2021-10-05 10:27:53");
System.out.println(parse3);
//{MinuteOfHour=27, NanoOfSecond=0, SecondOfMinute=53, HourOfAmPm=10, MicroOfSecond=0, MilliOfSecond=0},ISO resolved to 2021-10-05
}
}
构造器是私有的,方法是静态的,可以直接通过类名调用。
方法:
currentTimeMills():获取时间戳
exit(int status):status为0表示正常退出
gc():请求系统垃圾回收
getProperty(String key):返回String类型,常见的属性名以及属性的作用如下表:
BigInteger可以表示不可变的任意精度的整数(大数)
1、构造器:BigInteger(String val):根据字符串构建BigInteger对象
add():加,非静态
subtract():减,非静态
multiply():乘,非静态
divide():除,非静态
BigDecimal bd=new BigDecimal("11");
BigDecimal bd1=new BigDecimal("3");
System.out.println(bd.divide(bd1,3,BigDecimal.ROUND_HALF_DOWN)); //保留3个小数点,四舍五入
Comparable接口的使用举例:
1.像String、包装类等实现了Comparable接口,重写了compareTo(obj)方法,给出了比较两个对象的结果
2.像String、包装类重写compareTo()方法以后,进行了从小到大的排列
3.重写compareTo(obj)的规则:
如果当前对象this大于形参对象obj,则返回正整数,
如果当前对象this小于形参对象obj,则返回负整数,
如果当前对象this等于形参对象obj,则返回零。
4、对于自定义类来说,如果需要排序,我们可以让自定义类实现Comparable接口,重写compareTo(obj)方法中指明如何排序
5、重写后如果需要对自定义类实例化对象数组排序的话,接用Arrays.sort(对象数组)进行排序,输出数组需要使用Arrays.toString(对象数组),但也必须在自定义类中重写toString()方法,否则输出指针
(加上泛型的代码)
public class ComparableTest {
public static void main(String[] args) {
Person[] p = new Person[4];
p[0] = new Person("tom", 12);
p[1] = new Person("anna", 15);
p[2] = new Person("anby", 22);
p[3] = new Person("som", 12);
Arrays.sort(p);
System.out.println(Arrays.toString(p));
//[Person{name='anby', age=22}, Person{name='anna', age=15}, Person{name='tom', age=12}, Person{name='som', age=12}]
}
}
class Person implements Comparable<Person> {
String name;
int age;
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public void setName(String name) {
this.name = name;
}
public String getName() {
return name;
}
public Person(String name, int age) {
this.name = name;
this.age = age;
}
public Person() {
}
@Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", age=" + age +
'}';
}
@Override
public int compareTo(Person o) {
if (Integer.compare(this.age, o.age) == 0) { //年龄从大到小排序,年龄相同名字从大到小排序
return -this.name.compareTo(o.name);
}
return -Integer.compare(this.age, o.age);
}
}
Comparator接口:定制排序
重写compare(object o1,object o2)方法
o1>o2如果返回正整数
o1<o2如果返回负整数
o1=o2如果返回0
使用方式:Arrays.sort(数组,new 实现类对象);//将数组安装重写后的排序规则进行排序
/*
Comparator接口:定制排序
重写compare(object o1,object o2)方法
o1>o2如果返回正整数
o1<o2如果返回负整数
o1=o2如果返回0
使用方式:Arrays.sort(数组,new 实现类对象);//将数组安装重写后的排序规则进行排序
*/
例如:String从大到小排序
public class ComparatorTest {
public static void main(String[] args) {
String[] a = new String[]{"a", "bb", "c", "d", "ss", "ww", "e", "d", "f", "f"};
Arrays.sort(a, new Comparator() { //直接创建匿名实现类的匿名对象
@Override
public int compare(Object o1, Object o2) {
if ((o1 instanceof Object) && (o2 instanceof Object)) {
String s1 = (String) o1;
String s2 = (String) o2;
return -s1.compareTo(s2);
}
throw new RuntimeException("输入类型不一致");
}
});
System.out.println(Arrays.toString(a)); //[ww, ss, f, f, e, d, d, c, bb, a]
}
}
1、类的对象只有有限个,确定的时候,我们称此类为枚举类。例:
星期:Monday。。。Sunday
季节:Spring。。。Winter
2、当需要定义一组常量时,强烈建议使用枚举类。
3、 如果枚举类中只有一个常量,可以作为单例模式的实现方式
方式一:JDK5.0之前自定义
public class ComparatorTest {
public static void main(String[] args) {
Season str=Season.AUTUMN;
System.out.println(str); //Season{seasonName='秋天', seasonName1='秋风习习'}
}
}
class Season{
//1、声明Season对象的属性:private
private final String seasonName;
private final String seasonName1;
//2、私有化类的构造器,并给对象属性赋值
private Season(String seasonName,String seasonName1){
this.seasonName=seasonName;
this.seasonName1=seasonName1;
}
//3、提供当前枚举类的多个对象:public static final
public static final Season SPRING=new Season("春天","春暖花开");
public static final Season SUMMER=new Season("夏天","夏日炎炎");
public static final Season AUTUMN=new Season("秋天","秋风习习");
public static final Season WINTER=new Season("冬天","冬雪皑皑");
//4、其他诉求:获取枚举类属性、提供toString
public String getSeasonName() {
return seasonName;
}
public String getSeasonName1() {
return seasonName1;
}
@Override
public String toString() {
return "Season{" +
"seasonName='" + seasonName + '\'' +
", seasonName1='" + seasonName1 + '\'' +
'}';
}
}
方式二:JDK5.0,可以使用enum关键字定义
如何使用关键字enum定义枚举类
public class ComparatorTest {
public static void main(String[] args) {
Season autumn = Season.AUTUMN;
System.out.println(autumn); //AUTUMN,Enum类中的默认的toString是打印对象名
System.out.println(Season.class.getSuperclass()); //父类:class java.lang.Enum
}
}
//使用enum关键字
enum Season {
//1、提供当前枚举类的多个对象,用,隔开,结尾用分号隔开
SPRING("春天","春暖花开"),
SUMMER("夏天","夏日炎炎"),
AUTUMN("秋天","秋风习习"),
WINTER("冬天","冬雪皑皑");
//1、声明Season对象的属性:private
private final String seasonName;
private final String seasonName1;
//2、私有化类的构造器,并给对象属性赋值
private Season(String seasonName,String seasonName1) {
this.seasonName = seasonName;
this.seasonName1 = seasonName1;
}
//4、其他诉求:获取枚举类属性、提供toString
public String getSeasonName() {
return seasonName;
}
public String getSeasonName1() {
return seasonName1;
}
}
特别的:枚举类实现接口时除了正常的重写抽象方法外,还可以让每一个对象分别重写抽象方法,实现不同的效果。代码如下:
interface Info{ //接口
void show();
}
//使用enum关键字
enum Season implements Info{
//1、提供当前枚举类的多个对象,用,隔开,结尾用分号隔开
SPRING("春天", "春暖花开"){
@Override
public void show() {
System.out.println("春天");
}
},
SUMMER("夏天", "夏日炎炎"){
@Override
public void show() {
System.out.println("夏天");
}
},
AUTUMN("秋天", "秋风习习"){
@Override
public void show() {
System.out.println("秋天");
}
},
WINTER("冬天", "冬雪皑皑"){
@Override
public void show() {
System.out.println("冬天");
}
};
//2、声明Season对象的属性:private
private final String seasonName;
private final String seasonName1;
//3、私有化类的构造器,并给对象属性赋值
private Season(String seasonName, String seasonName1) {
this.seasonName = seasonName;
this.seasonName1 = seasonName1;
}
}
1、JDK5.0新增功能。格式@Xxx
2、代码里的特殊标记,在不改变代码的前提下,在源文件中嵌入一些补充信息。可用于修饰包、类、方法、成员变量、参数、局部变量的声明,这些信息保存在Annotation的“name=value”对中。
3、使用目的:标记过时的功能,忽略警告等,在JavaEE/Androw中占据了重要角色,例如用来配置应用程序的任何切面,代替了配置文件
4、框架=注解+反射+设计模式
@Deprecated修饰的方法会有个删除线,但是依旧可以使用
@SuppressWarnings(),括号内可以写多个成员变量。例:
@SuppressWarnings("unused"),意思是定义的还没有使用
@SuppressWarnings({"unused","rawtypes"}),意思是泛型和还没有使用
另外,Junit单元测试中也有大量注解的使用。简单罗列到下面,这里不再赘述。
@Test :标记在非静态的测试方法上。只有标记@Test的方法才能被作为一个测试方法单独测试。 -个类中可以有多个@Test标记的方法。运行时如果只想运行其中一个@Test标记的方法,那么选择这个方法名,然后单独运行,否则整个类的所有标记了@Test的方法都会被执行。
@Test(timeout= 1000) :设置超时时间,如果测试时间超过了你定义的timeout,测试失败
@Test(expected):申明出会发生的异常,比如@Test ( expected = Exception.class )
了解:
@BeforeClass :标记在静态方法上。因为这个方法只执行一次。在类初始化时执行。
@AfterClass :标记在静态方法上。因为这个方法只执行次。在所有方法完成后执行。
@Before :标记在非静态方法上。在@Test方法前面执行,而且是在每个@Test方法前面都执行
@After :标记在非静态方法上。在@Test方法后面执行,而且是在每个@Test方法后面都执行
@Ignore :标记在本次不参与测试的方法上。这个注解的含义就是"某些方法尚未完成,暂不参与此次测试”。
@BeforeClass、@AfterClass、 @Before、 @After、 @Ignore都是配合@Test它使用的 ,单独使用没有意义。
(参照SuppresWarnings)
1、默认继承了java.lang.annotation.Annotation接口
2、注解声明为@interface
3、成员变量以无参数方法的形式来声明,方法名和返回值定义了成员的名字和类型,类型只能是八种基本数据类型、String类型、Class类型、enum类型、Annotation类型、以上所有类型的数组。如果只有一个成员,建议使用参数名为value
4、指定成员变量初始值可使用default关键字、
5、如果自定义注解没有成员,表明是一个标识作用,如果有成员,在使用注解时,需要指明成员的值
自定义注解必须配上注解的信息处理流程(使用反射)才有意义。
public @interface MyAnnotation {
String value() default "hello";
}
修饰其他注解的注解叫做元注解。自定义注解一般都会知名两个注解:Retention、Target
1、Retention:表明生命周期,共有三种:
SOURCE:不会加载到.class中
CLASS:可以加载到.class 但不会继续保留到内存中。默认是这个
RUNTIME:可以到内存中,可以通过反射读取。
2、Target:用于修饰注解可以用于修饰哪些类型
3、Documented :表示所修饰的注解在被javadoc解析时保留下来,也就是可以保留在API中。
4、Inherited:被它修饰的注解具有继承性。父类的注解遗传给子类。
可重复注解:
jdk8之前的写法:
先自定义两个注解类
@Inherited
@Target({TYPE,FIELD,METHOD,PARAMETER,CONSTRUCTOR, LOCAL_VARIABLE})
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotation {
String value() default "hello";
}
@Inherited
@Retention(RetentionPolicy.RUNTIME)
@Target({TYPE,FIELD,METHOD,PARAMETER,CONSTRUCTOR, LOCAL_VARIABLE})
public @interface MyAnnotations {
MyAnnotation[] value();
}
调用时:@MyAnnotations({@MyAnnotation(value="hi"),@MyAnnotation(value="hello")})
jdk8的写法:
① 、在MyAnnotation上声明一个@Repeatable,成员值为MyAnnos.class
② 、 MyAnnotation的Retention、Target等元注解和MyAnnotations的元注解相同
定义两个注解类
@Repeatable(value = MyAnnotations.class)
@Inherited
@Target({TYPE,FIELD,METHOD,PARAMETER,CONSTRUCTOR, LOCAL_VARIABLE})
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotation {
String value() default "hello";
}
@Inherited
@Retention(RetentionPolicy.RUNTIME)
@Target({TYPE,FIELD,METHOD,PARAMETER,CONSTRUCTOR, LOCAL_VARIABLE})
public @interface MyAnnotations {
MyAnnotation[] value();
}
使用方式:
@MyAnnotation(value="hi")
@MyAnnotation(value="hello")
类型注解:
ElementType.TYPE_PARAMETER:表示该注解能写在类型变量的声明语句中,如:泛型声明
ElementType.TYPE_USE:表示该注解能写在使用类型的任何语句中。
2、 File类生命在java.io包下
4.后续File类的对象常会作为参数传递到流的构造器中,指明读取或写入的”终点".
代码:输出文件路径,并没有创建文件
public static void main(String[] args) {
//构造器1:
File file1=new File("hello.txt"); //相对路径,相对于当前nodule
File file2=new File("D:\\IDEA项目\\hi.txt"); //绝对路径
System.out.println(file1);//hello.txt
System.out.println(file2);//D:\IDEA项目\hi.txt
//构造器2
File file3=new File("D:\\IDEA项目","1.txt");
System.out.println(file3);//D:\IDEA项目\1.txt
//构造器3
File file4=new File(file3,"2.txt");
System.out.println(file4);//D:\IDEA项目\1.txt\2.txt
}
public Long lastModified() :获取最后一次修改的时间,毫秒数
public boolean isFile() :判断是否是文件
public boolean exists():判断是否存在
public booLean canRead() :判断是否可读
public boolean canWrite():判断是否可写
public boolean isHidden() :判断是否隐藏
移动文件:
public boolean renameTo(File dest):把文件重命名为指定的文件路径
比如:file1.renameTo(file2)为例,将file1重命名为file2
要想保证返回true,需要file1在硬盘中是存在的,file2是不存在的
public boolean createNewFile() :创建文件。若文件存在,则不创速,返回false
删除文件夹时必须先把文件夹内的文件删光才可以删除文件夹
删除注意事项:
Java中的删除不走回收站。
代码:
public static void main(String[] args) throws IOException {
File file1 = new File("hi.txt");
if (!file1.exists()) {
file1.createNewFile();
System.out.println("创建成功");
} else {//文件存在
file1.delete();
System.out.println("删除成功");
}
接口匿名对象的写法
public static void main(String[] args) {
//匿名函数
Comparator<Integer> integerComparator = new Comparator<Integer>() {
@Override
public int compare(Integer o1, Integer o2) {
return Integer.compare(o1, o2);
}
};
integerComparator.compare(12, 21);
//Lambda表达式的写法
//如果指明了参数类型,形参列表中的形参类型可以省略
//如果只有一个形参,()可以省略
//如果只有一条执行语句,{}和return都可以省略,{}省略的话,return必须也去掉
Comparator<Integer> integerComparator1 = (o1,o2) -> {
return Integer.compare(o1, o2);
};
integerComparator1.compare(12, 21);
//方法引用的写法
Comparator<Integer> integerComparator2 = Integer::compare;
integerComparator2.compare(12, 21);
}
-> :箭头操作符。
箭头左边:Lambda形参列表。
箭头右边:Lambda体(重写的抽象方法的方法体)
Lambda表达式写的都是接口中只有一个抽象方法的匿名实现对象,这种接口叫做函数式接口
接口中只有一个方法,此接口就是函数式接口
基于Lambda表达式 ,是函数式接口的实例
三种方法引用:
1、对象::非静态方法;
2、类名::静态方法;
3、类名::实例方法;
构造器引用
构造器引用:类名::new;
数组引用
数组类型[ ]::new;
生成Stream
public static void main(String[] args) {
//方式一:通过集合
// ArrayList<Employee> list = new ArrayList<>();
// list.add(new Employee(1, "Tom", 23, 20000));
// list.add(new Employee(2, "Jerry", 20, 10000));
// list.add(new Employee(3, "Rer", 22, 15000));
//通过指定集合
List<Employee> employees = null;
//default Stream<E> stream():返回一个顺序流
Stream<Employee> stream1 = employees.stream();
//default Stream<E> parallelstream():返回一个并行流
Stream<Employee> stream2 = employees.parallelStream();
//方式二:通过数组
int[] arr=new int[]{1,2,3,4,5,6,7,8,9,10};
//调用Arrays.stream
IntStream stream3 = Arrays.stream(arr);
Employee employee1=new Employee(12,"H",23,1000);
Employee employee2=new Employee(13,"Y",25,9000);
Employee[] e=new Employee[]{employee1,employee2};
Stream<Employee> stream4 = Arrays.stream(e);
//方式三:通过Stream.of
Stream<Integer> stream5 = Stream.of(1, 2, 3, 4, 5);
//方式四:创建无限流
//迭代
//public static<T> Stream<T> iterate(final T seed ,final UnaryOperator<T> f)
//遍历前10个偶数
//t->t+2,Lambda表达式传入参数t,返回t+2,limit是输出的个数,forEach是输出,使用了方法引用
Stream.iterate(0,t->t+2).limit(10).forEach(System.out::println);
//生成
//public static<T> Stream<T> generate(Supplier<T> s)
//Supplier是函数接口
Stream.generate(Math::random).limit(10).forEach(System.out::println);
}
筛选与切片
public static void main(String[] args) {
ArrayList<Employee> list = new ArrayList<>();
list.add(new Employee(1, "Tom", 23, 20000));
list.add(new Employee(2, "Jerry", 20, 10000));
list.add(new Employee(3, "Rer", 22, 15000));
List<Employee> employees = list;
Stream<Employee> stream1 = employees.stream();
//filter(Predicate p)接收Lambda,从流中排除某些元素
//使用函数接口Predicate,传入e,返回boolean型判断是否工资大于7000
stream1.filter(e->e.getSalary()>7000).forEach(System.out::println);
//limit(n),截断流,使元素不超过给定数量
list.stream().limit(2).forEach(System.out::println);
//skip(n),跳过元素,返回一个扔掉了前n个元素的流,如果流中没有元素。返回一个空流
list.stream().skip(2).forEach(System.out::println);
//distinct() 筛选,通过流所生成元素的hashcode()和equals()去除重复元素
list.add(new Employee(1, "Tom", 23, 20000));
list.stream().distinct().forEach(System.out::println);
}
映射
public class StreamTest {
public static void main(String[] args) {
List<String> list = Arrays.asList("aa", "bb", "cc", "dd");
//map(Function f)接收一个函数作为参数,将元素转换成其他形式或提取信息,该函数
list.stream().map(str -> str.toUpperCase()).forEach(System.out::println);
//练习:获取员工名字长度>3的信息
ArrayList<Employee> list1 = new ArrayList<>();
list1.add(new Employee(1, "Tom", 23, 20000));
list1.add(new Employee(2, "Jerry", 20, 10000));
list1.add(new Employee(3, "Rer", 22, 15000));
List<Employee> employees = list1;
Stream<Employee> stream = employees.stream();
//1、获取员工名字
Stream<String> stringStream = stream.map(e -> e.getName());
//2、筛选名字长度大于3的信息
stringStream.filter(e->e.length()>3).forEach(System.out::println);
// flatMap(Function) 接收一个函数作为参数,将流中每个值都换成另一个流,然后把所有流凑成一个流
//例如List集合list1中add另一个List集合list2,那么list1中的list2以集合的形式被添加,整个集合作为一个元素
//访问list2中的元素时需要使用两层for。map中如果放入的函数返回的是Stream类型,就会出现上述类似情况。
// 所以使用flatMap来避免这种情况。
//flatMap类似List集合中的addAll方法,在集合中添加集合时,会将集合中的元素作为单个元素添加进去,而不是整个集合添加进去
//使用骂跑出现Stream嵌套Stream的情况
Stream<Stream<Character>> streamStream = list.stream().map(StreamTest::stringToChar);
streamStream.forEach(s->{
s.forEach(System.out::println);
});
//使用flatMap
Stream<Character> characterStream = list.stream().flatMap(StreamTest::stringToChar);
characterStream.forEach(System.out::println);
}
public static Stream<Character> stringToChar(String str){
List<Character> list=null;
for(Character s:str.toCharArray()){
list.add(s);
}
return list.stream();
}
}
排序
public static void main(String[] args) {
// sorted:自然排序:无法排序自定义类型
List<Integer> integers = Arrays.asList(11, 23, 434, 54, 65, 9);
integers.stream().sorted().forEach(System.out::println);
// sorted(Comparator com):定制排序
ArrayList<Employee> list = new ArrayList<>();
list.add(new Employee(1, "Tom", 23, 20000));
list.add(new Employee(2, "Jerry", 20, 10000));
list.add(new Employee(2, "Yun", 19, 10008));
list.add(new Employee(3, "Rer", 22, 15000));
List<Employee> employees = list;
list.stream().sorted((o1, o2) -> {
if (o1.getAge() == o2.getAge())
return Double.compare(o1.getSalary(), o2.getSalary());
return Integer.compare(o1.getAge(), o2.getAge());
}
).forEach(System.out::println);
}
}
Stream的终止操作
1、匹配与查找
public static void main(String[] args) {
ArrayList<Employee> list = new ArrayList<>();
list.add(new Employee(1, "Tom", 23, 20000));
list.add(new Employee(2, "Jerry", 20, 10000));
list.add(new Employee(4, "Yun", 19, 10008));
list.add(new Employee(3, "Rer", 22, 15000));
List<Employee> employees = list;
//allMatch(Predicate p)-检查是否匹配所有元素。练习:是否所有的员工的年龄都大于18
boolean b = employees.stream().allMatch(e -> e.getAge() > 18);
System.out.println(b);//true
//anyMatch(Predicate p)-检查是否至少匹配一个元素。练习:是否存在员工的工资大于10000
boolean b1 = employees.stream().anyMatch(e -> e.getSalary() > 10000);
System.out.println(b1);//true
//noneMatch(Predicate p)-没有可匹配的元素返回true。练习:是否存在员工姓“雷”
boolean b2 = employees.stream().noneMatch(e -> (e.getName().contains("e")));
System.out.println(b2); //false
//findFirst-返回第一个元素
Optional<Employee> first = employees.stream().findFirst();
System.out.println(first);//Optional[Employee{id=1, name='Tom', age=23, salary=20000.0}]
//findAny-返回当前流中的任意元素
Optional<Employee> any = employees.parallelStream().findAny();
System.out.println(any);
//count-返回流中元素的总个数,中间可以放其他方法(终止方法除外)
long count = employees.stream().filter(e->e.getSalary()>10000).count();
System.out.println(count);//3
//max(Comparator c)-返回流中最大值,必须指出排序规则
//练习:返回最高的工资:
Stream<Double> doubleStream = employees.stream().map(e->e.getSalary());
Optional<Double> max = doubleStream.max((o1,o2)->Double.compare(o1,o2));
System.out.println(max);//Optional[20000.0]
//min(Comparator c)-返回流中最小值
//练习:返回最低工资的员工
Optional<Employee> min = employees.stream().min((e1, e2) -> Double.compare(e1.getSalary(), e2.getSalary()));
System.out.println(min);//Optional[Employee{id=2, name='Jerry', age=20, salary=10000.0}]
//forEach(Consumer c)-内部迭代
employees.stream().forEach(System.out::println);
//集合中的方法,与上述输出相同
employees.forEach(System.out::println);
}
2、归约
public static void main(String[] args) {
// reduce(T iden, BinaryOperator b)可以将流中元素反复结合起来,得到一个值。返回T
// 练习1、计算1-10自然数的和
List<Integer> list= Arrays.asList(1,2,3,4,5,6,7,8,9,10);
Integer reduce = list.stream().reduce(0, Integer::sum);
System.out.println(reduce);
// reduce(BinaryOperator b)可以将流中元素反复结合起来,得到一个值。返回Optional<T>
// 练习2:计算公司所有员工工资总和
ArrayList<Employee> list1 = new ArrayList<>();
list1.add(new Employee(1, "Tom", 23, 20000));
list1.add(new Employee(2, "Jerry", 20, 10000));
list1.add(new Employee(2, "Yun", 19, 10008));
list1.add(new Employee(3, "Rer", 22, 15000));
List<Employee> employees = list1;
Stream<Double> doubleStream = list1.stream().map(Employee::getSalary);
Optional<Double> reduce1 = doubleStream.reduce((r1,r2)->r1+r2);
System.out.println(reduce1);
}
收集
public static void main(String[] args) {
ArrayList<Employee> list = new ArrayList<>();
list.add(new Employee(1, "Tom", 23, 20000));
list.add(new Employee(2, "Jerry", 20, 10000));
list.add(new Employee(2, "Yun", 19, 10008));
list.add(new Employee(3, "Rer", 22, 15000));
List<Employee> employees = list;
//collect(Collector c)将流转换为其他形式,接收一个Collector接口的实现
//练习1:查找工资大于10000的员工,结果返回一个List或Set
Stream<Employee> employeeStream = list.stream().filter(e -> e.getSalary() > 10000);
List<Employee> employeeList = employeeStream.collect(Collectors.toList());
employeeList.forEach(System.out::println);
Set<Employee> employeeSet = employeeStream.collect(Collectors.toSet());
employeeSet.forEach(System.out::println);
}
避免空指针异常
常用方法
ofNullable
orElse
public static void main(String[] args) {
Girl girl=null;
// System.out.println(girl.getName());//空指针异常
//修改
//ofNullable()包装可能会空指针异常的对象,防止空指针异常,但不会为对象赋值
Optional<Girl> girlOptional = Optional.ofNullable(girl);
System.out.println(girlOptional);//Optional.empty
//orElse,如果调用者为空,则返回参数
Girl girl1 = girlOptional.orElse(new Girl("赵丽颖"));
System.out.println(girl1.getName());
}
新增方法
模块
try-catch
jshell
接口中的私有方法
集合工厂方法:创建只读集合
Arrays.asList()方法添加元素形成的集合是只读集合,不可以再修改
Set.of()...
Map.of()...
Map.ofEntries(Map.Entry e1,Map.Entry e2)....
InputStream的新方法tranferTo()
把输入流中的说有数据直接自动复制到输出流中
增强的Stream
takeWhile(Predicate p)方法.返回从开头开始的按照指定规则尽量多的元素
dropWhile(Predicate p)。跳过满足指定规则的元素,返回剩余的元素
ofNullable()。实例化Stream时可以使用Stream.of(元素)的方式,但如果元素只是一个null,会出现异常。为了防止这种情况提供了ofNullable()。相当于Optional类中的ofNullable方法
重载的iterate
重载的iterate方法(创建Stream实例时方法四无限流使用的方法)
重载后方法为 iterate(0,x<100,x->x+1);中间添加了一个约束参数
Optional类提供了Stream方法
局部变量类型推断
根据等号右边的赋值类型推断出左边,所以左边的类型可以用Var代替
以下这些方法不能使用Var
1、变量没有赋值时
2、右边是Lambda表示式
3、静态初始化时
4、使用方法引用时
集合中新增的copyOf(),用于创建一个只读集合
public static void main(String[] args) {
var list = List.of("java", "python", "c");
var copy1 = List.copyOf(list);
System.out.println(copy1); //[java, python, c]
System.out.println(list==copy1); //true
var list1 = new ArrayList<>();
list1.add("aaa");
var copy2 = List.copyOf(list1);
System.out.println(copy2); //[aaa]
System.out.println(copy2==list1); //false
}
//如果集合是只读集合,那么经过copyof处理后的只读集合与该集合相同,否则返回一个新集合,与原集合不同
String新增方法
isBlank():判断字符串是否是空白
strip():去掉首尾空格
stripTrailing() 去掉尾部空格
stripLeading() 去掉首部空格
repeat(int count) 复制字符串
lines().count() 行数统计
Optional增强
局部变量推断升级
使用var可以给lambda表达式形参加上注解,格式(注解 var 形参)
全新的HTTP客户端API
HttpClient替换HttpURLConnection
更加简洁的编译运行方式
原方式
先编译
javac 文件名.java -->生成字节码文件 文件名.class
后运行
java 文件名
现在
java 文件名.java
要求:文件中第一个类必须有main方法,默认执行第一个类
废除Nashorn引擎
java Flight Recorder 飞行记录仪
持续更新中。。。。。
因篇幅问题不能全部显示,请点此查看更多更全内容