java语法
编译型程序通过编译链接生成可执行文件,在os上运行。
java源程序.java通过编译生成字节码程序.class,在解释器上解释执行
jdk:包含jre和开发工具运行时环境。开发全新java程序要安装jdk
javac编译文件为class文件,可用java命令运行。
javac demo.java
java demo//执行
数据类型
类型变量
局部变量
成员变量
类变量:类中,方法体之外,static
package语句在源文件首行
java文件
一个源文件只有一个public类,可以有多个非public类,源文件名和public保持一致
标识符
数字,字母,下划线,$组成,不能以数字开头,不能是关键字,
命名规则:方法、变量是首字母小写。类首字母大写
数据类型
内置数据类型
Byte.SIZE Byte.MIN_SIZE
引用数据类型
引用类型指向一个对象
Site site = new Site("s");左边是引用变量
常量
final关键字修饰
final double PI = 3.1415927;
变量
类变量:static,类只有类变量的一份拷贝,可以类名.访问
实例变量:类中
局部变量:栈上分配
包装类&Number&Math类
每一个内置数据类型提供了对应的包装类。
Math.方法
类型转换
自动类型转换:
不对boolen,容量大的转容量小的必须强制转换
强制类型转换:
int a = int(88.88)//不强转会报错
##
访问控制
default同一包内可见
private同一类可见,不能修饰外部类
public所有类可见
protected同一包内的类和所有子类可见,不能修饰外部类。子类与基类在同一包中:被声明为 protected 的变量、方法和构造器能被同一个包中的任何其他类访问;子类与基类不在同一包中:那么在子类中,子类实例可以访问其从基类继承而来的 protected 方法,而不能访问基类实例的protected方法。不能修饰类,不能修饰接口和接口的成员变量/方法。如果我们只想让该方法对其所在类的子类可见,则将该方法声明为 protected
默认:同一个包内可见
修饰符
synchronized修饰符:声明的方法同一时间只能被一个线程访问
transient修饰符:序列化的对象包含被 transient 修饰的实例变量时,java 虚拟机(JVM)跳过该特定的变量。,不会持久化
volatile 修饰的成员变量在每次被线程访问时,都强制从共享内存中重新读取该成员变量的值,变量发生变化时,会强制将变量值写回到共享内存。
访问控制继承规则
父类中声明为 public 的方法在子类中也必须为 public。
父类中声明为 protected 的方法在子类中要么声明为 protected,要么声明为 public,不能声明为 private。
父类中声明为 private 的方法,不能够被子类继承。
运算符
增强for
for(intx : numbers ){
System.out.print( x );
System.out.print(",");
}
字符操作
类型会自动提升:byte,char ,shot -->int->long->float->double
字符串操作
字符串+anything:拼接
赋值运算符隐含了强转
& 和&&都是逻辑运算符,但是后者是短路的
a>b?a:b;
instanceod运算符:
Stringname="James";
booleanresult=nameinstanceofString; // 由于 name 是 String 类型,所以返回真
##
Character类
char类型的包装类
2.数组
2.1 定义
int [] arr
int arr[]
2.2 数组动态初始化
int [] arr = new int [3];3个
2.3数组静态初始化
int [] arr = new int [] {1,2,3};
初始化时指定每个元素的值,由系统指定长度
int [] arr ={1,2,3}
2.4 数组遍历
arr.length 数组长度
2.5 数组作为参数
Arrays类
方法都是静态的
toString()方法:返回字符串格式
sort(),数组排序
equals()
binarySearch()//对排序好的进行二分查找
引用传递数组名,可以用引用修改内容,但是函数里修改数组地址无法传回
java内存分配
栈内存:存储局部变量
堆内存:存储new出来的实体和对象,new出来的东西会有地址值,使用完毕会在垃圾回收器空闲时回收。
方法
publicstaticintfuntion(intnum){
//方法体
}
方法调用时,参数的数量和类型必须和定义一致
形参:方法定义的参数
实参:方法调用的参数
方法不能嵌套定义
方法重载
同一个类中,方法具有相同的方法名,但参数类型或者数量不同
引用类型:
finalize方法
对象被垃圾收集器析构(回收)之前调用,这个方法叫做 finalize( ),它用来清除回收对象。
protected void finalize() { // 在这里终结代码 }
类和对象
publicclassA{
}
Aa=newA();
private
private成员变量可以提供get和set方法。最好用编译器自带的setget
给类写测试类:ADemo
类函数中,局部变量赋值给成员变量:this.name = name;
this 内存原理
枚举
enumColor
{
RED, GREEN, BLUE;
}
Color.RED
enum继承的方法:
values() 返回枚举类中所有的值。
ordinal()方法可以找到每个枚举常量的索引,就像数组索引一样。
valueOf()方法返回指定字符串值的枚举常量。
面向对象
三大特征:封装,继承,多态
继承
不支持多继承,可多重继承,默认继承object
extends关键字
implements关键字:可以同时继承多个接口
super this
super关键字来实现对父类成员的访问,用来引用当前对象的父类
this:自己
构造方法
方法名同类名
publicclassStudent{
publicvoidStudent(){
}
}
给了构造方法后,系统不会给默认的构造方法了
通过对象调用方法时,返回值:ctrl+alt+v自动生成
子类是不继承父类的构造器(构造方法或者构造函数)的,它只是调用(隐式或显式)。如果父类的构造器带有参数,则必须在子类的构造器中显式地通过 super 关键字调用父类的构造器并配以适当的参数列表。
String类
在java.lang包下,使用时不需要导入包
java中所有双引号对象都是string类对象
字符串不可变,在创建后不能更改。但是可以共享。
注意:String 类是不可改变的,所以你一旦创建了 String 对象,那它的值就无法改变了(详看笔记部分解析)。
如果需要对字符串做很多修改,那么应该选择使用 StringBuffer & StringBuilder 类。
string创建的字符串存储在公共池中,new创建的字符串对象在堆上。
string构造
String s1 = new String();
char [] chs = {'a','b'};
String s2 =new String (chs);//数组构造
byte[] bys = {1,2,3};
String s3 = new Stirng(bys);
String s4 = "abc";
以“”方式给出的字符串,只要字符序列相同,无论在代码中出现几次,jvm只会建立一个sting对象,并在字符串池中维护。
字符串比较
==比较的地址
方法equals()比较的内容
字符串类常用方法
charAt(int index)方法,根据索引返回char值。
length()
concat()
euqals()
indexOf(char ch)返回第一次出现的索引
indexOf(char ,int 指定索引处开始)
split(" x");指定字符split
[boolean endsWith(String suffix)]
格式化字符串
输出格式化,调用string的静态方法format(),返回string对象
StringBuffer
在使用 StringBuffer 类时,每次都会对 StringBuffer 对象本身进行操作,而不是生成新的对象,所以如果需要对字符串进行修改推荐使用 StringBuffer
方法:
append
reverse
delete(start,end)
insert(offset,int/string)
replace(start,end,string)
capacity()
ensureCapacity
indexOf(str)
StringBuilder
可变字符串类:StirngBuilder:内容可变。StringBuilder 的方法不是线程安全的(不能同步访问)StringBuilder 相较于 StringBuffer 有速度优势,所以多数情况下建议使用 StringBuilder 类,应用程序要求线程安全的情况下,则必须使用 StringBuffer 类。
构造:
StringBuilder sb = new StringBuilder();
sb.append(任意类型);//添加对象,返回对象本身。4
sb.reverse();//反转
StringBuilder和String的转换
因为前者有append,reverse方法
1.StringBuilder 转为String :
public String toString();//通过方法
2.String转为StringBuilder
public StringBuilder(String s);//通过构造方法
eg:
StringBuilder sb = new StringBuilder(s);
ArrayList集合
存储的数据容量可以改变,可调整数组大小
构造和添加
ArrayList<String> arr = new ArrayList<String>();//构造
//输出集合,直接输出arr,输出样子为[]
arr.add(E e);//添加元素到末尾
arr.add(int index ,E e);//指定位置插入元素,注意索引越界
常用方法
remove();
E remove(int index);
E set(int index , E e);
E get(int index);
int size();
继承
public class Son extends Father{
}
继承中变量访问特点:在子类方法中访问一个变量:子类局部-子类成员-父类成员-错误
super
this.x访问本类成员
super.x访问父类成员super(),super.xx()
构造方法
子类对象要访问父类的无参构造方法。所以子类的构造方法第一条默认语句super();
成员方法
同成员变量
方法重写
@Override注解检查是否是重写的 方法
子类不能重写私有方法,重写的访问权限不能比父类低
继承注意事项
不能继承多个类,但是可以多层继承
包
对类进行分类管理,区分类名的命名空间,访问控制
package com.sicnu//属于这个包
public class C{
...
}
直接编译java c会报错,因为找不到包
要把这个类放进这个包里
javac C
java com.sicnu.C//执行
javac -d . C.java//自动建包
java com.sicnu.C
import 包
修饰符
public
protected
private
在同一个包下除了private都能访问,在不同包无关的类只能访问public,不同包子类可以访问protected和public
final
修饰的方法不能重写
修饰的成员变量不能被赋值,是一个常量。
修饰类则不能继承该类
修饰局部变量:
static
修饰成员变量:被所有变量共享,可以用类名.x访问
静态的成员方法只能访问静态的成员变量和方法
多态
同一个对象在不同时刻表现不同的形态
前提是继承/实现关系,有方法重写,有父类引用/接口指向子类对象,但是不能访问子类特有的功能
Animal a = new Cat();//当首先检查父类中是否有该方法,如果没有,则编译错误;如果有,再去调用子类的同名方法。
好处:避免重写代码,比如:猫,狗都有eat的方法,每次调用的时候,可以用他们的父类,使用时实际是子类型。弊端:不能使用子类的特有功能
虚函数
多态的转型
向上转
animal a = new cat();//向上转
a.playgame();//错误
//向下转
cat c = cat(a);
c.playgame();//对
//注意类转换异常
抽象类
public abstract class Animal {//抽象类,使用abstract
public abstract void eat();//抽象方法
public void sleep(){//可以有或者只有非抽象方法
}
}
//创建对象,参照多态,子类重写抽象方法,通过子类对象实例化;否则子类也是抽象类,就不重写抽象方法
封装
修改属性为private
getset方法
接口
行为的抽象。接口是抽象的,不能直接实例化,但是可以通过多态方式实例化,这叫接口多态。
接口可以多继承
public interface jiekou{//定义一个接口
public static final int a = 0;
public abstract func(){
}
//可以不申明抽象,接口是隐式抽象
}
pubLic class Cat implements jiekou{//类实现接口,如果不重写接口,则这个类定义为抽像类
类实现接口方法时,不能抛出强制性异常,只能在接口中,或者继承接口的抽象类中抛出该强制性异常。
}
//测试类
public class jiekouDemo{
jiekou j = new Cat();//使用多态的方式实例化接口
}
标记接口
标记接口是没有任何方法和属性的接口.它仅仅表明它的类属于一个特定的类型。
用于建立公共的父接口,可以使得实现该接口的类变成一个多态类型的接口。
接口的成员特点
接口的实现类:jiekouImpl{},接口的成员变量默认常量,可以直接通过接口类名访问成员。接口没有构造方法,但是实现它的类,构造super是构造的object类的方法。
成员方法也只能是抽象的方法。
类和接口的关系
pubLic classA extends classF implements interA, interB{}
抽象类和接口的区别
抽象类是对类的抽象,包括属性和行为。接口是对行为的抽象,主要是行为。
形参和返回值
接口和抽象类不能实例化,所以作为形参的时候,要用它的实现类对象。
内部类
在一个类中定义的类就是内部类。
内部类可以直接访问外部类的成员,包括私有。外部类要访问内部类的成员,必须创建对象。内部类可以对同一个包中的其他类隐藏。
成员内部类
成员内部类:在类的成员的位置。一般为私有的。
创建对象:外部类名.内部类名 对象名 = 外部类对象.内部类对象
Out.Iner oi = new Out().new Iner();//一般为私有,不能直接调用
局部内部类
方法里面定义内部类,外部无法直接使用,需要在方法里创建对象使用。这个类可以访问外部类的成员,也可以访问方法内的局部变量
匿名内部类
public class A{
public void method(){
new Inter(){//直接new类名,同局部累不累的
public void show();//重写的方法
}.show();//调用方法
//使用方法
Interface jiekou = new jiekou(){//这是一个匿名内部类,实现接口的方法。
public void jiekouFunc();//重写接口的方法
}
jiekou.jiekouFunc();//正确调用方法
}
}
匿名内部类实质是对象,用于接口回调。
本质是继承了该类或者实现了该接口的子类匿名对象。实际上对象,直接调方法.可以作为实参。
匿名对象
//匿名对象举例
new student();
new student().show();
应用于实参传递,能够减少栈帧的分配和指向,且在调用完毕后能够被GC机制(垃圾回收机制)快速的回收
System 类
全是静态方法,通过类名直接调用。
System.currentTimeMillis();
Object类
类层次结构的根。构造方法无参。//ctrl+B 看方法源码
toString()方法继承自Object,所有类重写这个方法。alt+inset.自动生成
equals()方法。比较对象内容,重写equals()方法。自动生成
public boolean equals(Object o){
if(this == 0) return True;
if(o==null || getClass() != o.getClass())
return false;
}
基本类型包装类
int--Integer类
方法:
public Integer(int value);根据int值创建Integer对象
public Integer(String s);根据String值创建Integer对象
public static Integer valueOf(int i);//返回指定对象的Integer实例
public static Integer valueOf(String s)//字符串必须是数字组成的
//int转Integer类型
Integer i1 = Integer.valueOf(100);
Integer i2 = Integer.valueOf("100");
基本类型包装类常见操作:用于基本类型和字符串的相互转换。
int和String的相互转换
//方式1
String s1 = ""+100;
//方式2:publIC static String valueOf(int i)
String s2 = String.valueOf(100);
String 转Int
//方式1,先转Integer,再转INt
Integer i = Integer.valueOf("100");
int x = i.intValue();
//方式2 pubLic Static int parseInt(String s);
int y = Iteger.parseInt("100");
自动装箱和拆箱
装箱:基本的数据类型转为对应的包装类型
拆箱:包装类类型转为基本类型
Integer i = Integer.valueOf(100);
Integer i1 = 100;//直接把int赋值给Integer类型,自动装箱
i1 += 200;//隐含自动拆箱和装箱
日期类
Date类
毫秒为精度。
方法:
public long getTime()
boolen after(Date)
boolen before(Date)
public void setTime(long time);//设置时间,给毫秒值
toString()
SlimpleDateFormat类
允许你选择任何用户自定义日期时间格式来运行。例如:
import java.util.*;
import java.text.*;
public class DateDemo {
public static void main(String[] args) {
Date dNow = new Date( );
SimpleDateFormat ft = new SimpleDateFormat ("yyyy-MM-dd hh:mm:ss");
System.out.println("当前时间为: " + ft.format(dNow));
}
}
日期和时间有格式化编码
java休眠:
sleep()
Stream File和IO
IO流
读:Read,Input,InputStream
写:Output,OutputStream,Write
字节流:按照字节的方式读取数据,一次读取一个字节byte,等同于一次读取8个二进制位,这种流是万能的,什么类型的文件都可以读取,包括:文本文件、图片、声音文件、视频文件
字符流:按照字符的方式读取数据,一次读取一个字符,这种流是为了方便读取普通文本文件而存在的,但这种流不能读取:图片、声音、视频等文件,只能读取纯文本文件,也读不了word文件
类名以“Stream”结尾的都是字节流,以“Reader/Writer”结尾的都是字符流
输出流在最终输出完后一定要记得flush()方法刷新一下,这个刷新表示将通道当中剩余未输出的数据强行输出完(清空通道)
读取控制台的输入
BufferedReader br = new BufferedReader(new
InputStreamReader(System.in));
使用 read() 方法从控制台读取一个字符,或者用 readLine() 方法读取一个字符串。
控制台输出
System.out.write(b);
读写文件
IO流分为字符流和字节流
FileInputStream
常用方法
1、返回流当中剩余的没有读到的字节数量
public int available();
2、跳过n个字节不读
public long skip(long n);
3、返回读到的字节值,每次读一个字节,读完自动后移,文件读完返回-1
public int read();
4、将读到的内容存入字节数组中,且最多读入b.length个字节,返回读到的字节总数,文件读完返回-1
public int read(byte[] b);
InputStream f = new FileInputStream("C:/java/hello");
//或者
File f = new File("C:/java/hello");
InputStream in = new FileInputStream(f);
in.read(int )
in.close()
FileOutputStream
不存在就创建文件
常用方法
1、将指定 byte 数组中从偏移量 off 开始的 len 个字节写入此文件输出流
public void write(byte[] b, int off, int len);
OutputStream f = new FileOutputStream("C:/java/hello");
// File f = new File("C:/java/hello");
OutputStream fOut = new FileOutputStream(f);
转换流
通过构造方法将字节输入流转换为字符输入流public InputStreamReader(InputStream in);
通过构造方法将字节输出流转换为字符输出流public OutputStreamWriter(OutputStream out);
当一个流的构造方法中需要一个流的时候,这个被传进来的流叫作节点流,外部负责包装的这个流叫包装流或处理流,只需关闭包装流就行,当包装流关闭后节点流会自动关闭
缓冲流
缓冲流自带缓冲区,不需要再定义数组
BufferedReader
一次读取一个文本行,包含该行内容的字符串,不包含任何行终止符,如果已到达文本末尾,则返回 null
public String readLine();
BufferedWriter
BufferedInputStream
BufferedOutputStream
数据流
数据流中DateOutputStream可以将数据连同数据的类型一并写入文件中,这个文件不是普通的文本文件,用记事本打不开,只能使用DateInputStream去读,并且读的时候需要与写的顺序一致才可以正常取出数据
标准输出流
printStream
printWriter
默认输出到控制台
改变输出方向到指定文本:
PrintStream ps = new PrintStream(new FileOutputStream("src/lianxi_io/myio_1", true));
//改变输出方向
System.setOut(ps);
序列化和反序列化
序列化:Java对象按照流的方式存入文本文件或网络中,将Java对象的状态保存下来的过程,通过ObjectOutputStream完成(对象---->流数据)
反序列化:将硬盘上的流数据重新恢复到内存中,恢复成Java对象,通过ObjectInputStream完成(流数据---->对象)
参与序列化和反序列化的对象必须实现serializable接口(标记接口,无内容)
serializable接口是标志接口,里面啥也没有,用来给JVM参考,当JVM识别到这个接口后,就会为实现该接口的类自动生成一个序列化版本号用来标识类;Java虚拟机识别一个类的时候先通过类名,如果类名一致就再通过序列化版本号;凡是一个类实现了serializable接口,建议手动给该类提供一个固定不变的序列化版本号,这样,以后这个类即使被修改了但是版本号没有变,Java虚拟机也会认为是同一类
private static final long serialVersionUID= 1L;
在实现serializable接口的类中若不想某个成员变量被序列化,可用transient关键字声明
序列化后如果修改了对象的类文件
序列化:
ObjectOutputStream(OutputStream out):创建一个写入指定的OutputStream的ObjectOutputStream
法1:序列化,使用ObjectOutputStream在测试类中{
ObjectOutputStream oos = null;
try{
//序列化
oos = new ObjectOutputStream(new FileOutputStream("src/lianxi_io/myio_1"));
//序列化方法,把student对象序列化
oos.writeObject(new Student("xss", 23));
oos.writeObject(new Student("xxx", 34));
oos.flush();
} catch (FileNotFoundException e){
e.printStackTrace();
} catch (IOException e){
e.printStackTrace();
} finally {
try{
oos.close();
} catch (IOException e){
e.printStackTrace();
}
}
}
同时,类实现序列化接口(接口是标记接口,没有方法)
class Student implements Serializable
{
//手动固定序列化版本号
private static final long serialVersionUID = 2L;
private String name;
private int age;
private String address;
public Student()
{
}
public Student(String name, int age)
{
super();
this.name = name;
this.age = age;
}
public String toString()
{
return "Student [name=" + name + ", age=" + age + "]";
}
}
反序列化:ObjectInputStream(InputStream in),创建从指定InputStream读取得到ObjectInputStream
反序列化
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.ObjectInputStream;
public class lianxi
{
public static void main(String[] args)
{
ObjectInputStream ois = null;
try{
//反序列化
ois = new ObjectInputStream(new FileInputStream("src/lianxi_io/myio_1"));
//反序列化方法,readObjectt()
Object obj1 = ois.readObject();
System.out.println(obj1);
Student s1 = Student(obj1);
Object obj2 = ois.readObject();
System.out.println(obj2);
} catch (FileNotFoundException e){
e.printStackTrace();
} catch (IOException e){
e.printStackTrace();
} catch (ClassNotFoundException e){
e.printStackTrace();
} finally {
try{
ois.close();
} catch (IOException e){
e.printStackTrace();
}
}
}
}
File类
File的构造方法
1、根据一个路径得到FIle对象
public File(String pathname);
2、根据一个目录和一个子文件/目录得到File对象
public File(String parent, String child);
3、根据一个父File对象和一个子文件/目录得到对象
public File(File parent, String child);
创建
1、创建文件,如果已存在该文件就不再创建并返回false
public boolean createNewFile();
2、创建文件夹,如果已存在该文件就不再创建并返回false
public boolean mkdir();
3、创建文件夹,且是多级创建,如果父文件夹不存在就创建
public boolean mkdirs()
4、删除文件或文件夹
public boolean delete();
5、重命名
public boolean renameTo(File dest);
获取
1、获取绝对路径
public String getAbsolutePath();
2、获取相对路径
public String getPath();
3、获取名称
public String getName();
4、获取文件大小
public long length();
5、获取最后一次修改时间,毫秒值
public long lastModified();
高级获取
1、获取指定目录下的所有文件或文件夹的名称数组
public String[] list();
2、获取指定目录下的所有文件或文件夹的File数组
public File[] listFiles();
目录
File类的mkdir()创建一个文件夹
mkdirs()创建一个文件夹和它的所有父文件夹。
String dirname = "/tmp/user/java/bin";
File d = new File(dirname);
// 现在创建目录
d.mkdirs();
目录是File对象,那么调用 isDirectory() 方法会返回 true,调用该对象上的 list() 方法,来提取它包含的文件和文件夹的列表
删除目录或文件
java.io.File.delete() 方法。该目录下没有其他文件才能正确删除
java正则
\s+ :可以匹配多个空格
.:任意的一个字符
^ :以什么开始
\d+ :匹配一个或者多个数字
\. : 匹配“.”
()?设置括号内得选项可选
java.util.regex包:
1.Pattern类
对象是一个正则表达式,
String content = "I am noob " +
"from runoob.com.";
String pattern = ".*runoob.*";
boolean isMatch = Pattern.matches(pattern, content);
2.Matcher类
Matcher 对象是对输入字符串进行解释和匹配操作的引擎
// 按指定模式在字符串查找
String line = "This order was placed for QT3000! OK?";
String pattern = "(\\D*)(\\d+)(.*)";
// 创建 Pattern 对象
Pattern r = Pattern.compile(pattern);
// 现在创建 matcher 对象
Matcher m = r.matcher(line);
3.PatternSyntaxException类
(正则的语法错误)
异常
1.检查性异常:用户错误,编译时
2.运行时异常:
3.错误:栈溢出
所有的异常类是从 java.lang.Exception 类继承的子类。
不抛出去,JVM会默认处理,程序报错。
捕获异常
try {
//代码
}catch (异常类名 变量名){
异常处理代码
}
try{
// 程序代码
}catch(异常类型1 异常的变量名1){
// 程序代码
}catch(异常类型2 异常的变量名2){
// 程序代码
}catch(异常类型3 异常的变量名3){
// 程序代码
}finally{
//必然执行
}
throwable
方法:
pubLic String getMessage();//返回此类的详细消息字符串
public String toString();//返回可抛出的简短描述
public void printStaxkTrace();//把异常的错误信息输出在控制台。
编译时异常和运行时异常:
throws处理异常
throws 异常类名;跟在方法的括号后面.对于运行时异常,不能继续执行代码。对于编译时异常,一样。谁调用谁处理。
public func() throws Exception{}
自定义异常
public class 异常类名 extends Exception{
无参构造
有参构造
}
public class ScoreException extends Exception{
public ScoreException(){}
public ScoreException(String message){
super(message);
}
}
public class Teacher{
public void checkScore(int score) throws ScoreException{
if(score <0){
throw new ScoreException();//执行时一定发生了异常
}else{
...
}
}
}
集合
存储空间可变
单列集合:collection,可重复:list集合 / 不可重复:set集合
Collection{抽象类
List{有序的,类似数组
LinkList
ArrayList链表数组,不同步,性能好于Vector
Vector线程同步的
Stack
}
Set{
HashSet
TreeSet
}
}
双列集合:map
collection集合
不能直接实现。
常用函数:
1、添加功能
boolean add(Object obj);//添加一个元素
boolean addAll(Collection c);//添加一个集合的元素
2、删除功能
void clear();//移除所有元素
boolean remove(Object o);//移除一个元素
boolean removeAll(Collection c);//移除一个集合的元素,只要有一个元素被移除就返回true
3、判断功能
boolean contains(Object o);//判断集合中是否包含指定元素
boolean containsAll(Collection c);//判断集合中是否包含指定的集合元素,只有包含所有元素才叫包含,才返回true
boolean isEmpty();//判断集合是否为空
4、长度功能
int size();//这里获取的是元素的实际个数,不是集合的容量
5、交集功能
/*
设有两个集合A、B
A.retainAll(B);
若A、B之间有交集就把交集保存到集合A中,若无交集,那么集合A就为空,
至于返回结果则取决于保存交集的A集与保存之前的集合内容是否有变化,
有变化就返回true,没有变化就返回false
*/
boolean retainAll(Collection c);
6、集合转数组
Object[] toArray();//转型后的数组中每一个元素都是Object类型的
alt+7可以看到类的所有方法
迭代器
集合的遍历方法Iterator < E> iterator();,迭代器是个接口,因为每个集合得数据结构不同,存储和遍历方式不同,接口的实现类是以内部类的方式体现。
iterator常用方法:
next():返回下一个元素
hasNext():返回是否有下一个
Collection c = new ArrayList();
方式一:
Iterator it = c.iterator();//通过集合对象获取迭代器对象
while(it.hasNext())
{
System.out.println(it.next());
}
ArrayList
重写了toStirng方法,可以直接输出
Collection<String> c = new ArrayList<String>();
//添加元素等常用方法
c.add("a");//返回true
c.remove("a");
c.clear();
if(c.contain("a")){}
c.isEmpty()
c.size;
遍历
iterator:迭代器,集合专用的遍历方式
集合的方法:Iterator <E> iterator(),返回此集合中元素的迭代器,通过集合的这个方法得到
iterator常用方法:
next():返回下一个元素
hasNext():返回是否有下一个
Collection<String> c = new ArrayList<String>();
Iterator<String> it = c.iterator();//迭代器的类型同生成他的集合的类型一致
it.next();
while(it.hasNext()){
String s = it.next();
}
List集合
有序集合,序列。有索引,允许重复元素,是个接口
List<String> list = new ArrayList<String>();//多态的方式定义对象
list.add("a");
System.out.println(list);//重写了toString
//遍历
Iterator<Stirng> it = list.iterator();
list特有方法:它的子类都有
add(index,E)
E remove(index);
E set(index,E)
E get(index)
//遍历集合方式2
for(int i=0;i<list.size();i++){
list.get(i);
}
并发修改异常:
迭代器遍历的过程中,通过list.add添加了元素,造成了预期修改的次数和修改的次数不一致
所以使用for循环
List列表迭代器
ListIterator,继承自Iterator。允许任意方向遍历列表
ListIterator方法:hasNext(),next(),hasPrevious(),previous(),add(E)
ListIterator<String> it =List.ListIterator();
//可以用it.add()添加元素,ListIterator可以实现逆向遍历,但是必须同一迭代器正向遍历之后才能进行逆向遍历,无实际意义
增强for循环
for(元素类型 变量名: 数组或者Collection集合){
//在此处使用变量,变量就是元素
}
内部就是一个Iterator迭代器
int[] arr ={1,2,3,4,5};
for(int i:arr){
System.out.println(i);
}
List<String> list = new ArrayList<String>;
for(Stirng s:list){
}
List集合子类
ArrayList集合
底层是数组,查询快。
LinkedList集合
底层链表。
LikedList特有方法:
addFirst(E);
addLast(E);
getFirst()
getLast();
removeFirst();
removeLast();
Set集合
继承自Collection,不能重复,且没有索引,是接口
HashSet 对集合的迭代顺序不保证
Set<String> set = new HashSet<String>();
set.add("s");
//遍历
for(Stirng s :set){
s;
}
HashSet
Object类的方法:hashCode():返回对象的哈希码值。
底层结构式hasj表,没有索引
如果存入的对象一样,就不存。但是如果是自己写的类对象,需要重写equal方法。
LinkedHashSet
由链表保证元素有序,由哈希表保证元素唯一。
LinkedHashSet<String> linkedhaset = new LinkedHashSet<String>();
TreeSet
元素有序,排序方式由构造方法决定。
TreeSet():根据元素自然顺序进行排序
TreeSet(Comparator comparator):根据指定的比较器进行排序
没有索引,不包含重复元素.
TreeSet<Integer> ts = new TreeSet<Integer>();
对于comparator接口,相应的类要实现这个接口。
public class Student implements Comparable<Student>{
@Override
public int comparabTo(Student s){
}
}
TreeSet
底层是一个TreeMap,二叉树。元素无序不重复,可以自动排序,TreeSet集合自动排序的特点是通过实现java.lang包下Comparable接口或java.util包下的Comparator接口来完成的,即要指定比较规则。因为TreeSet< Classx > classx = newTreeSet< Classx>();当添加一个classx元素时,需要进行比较。比较方法:
法1:实现接口Comparable的compareTo方法。适用于只有一个比较规则
返回值0表示相同,value回覆盖。返回>0,在右子树查找
法2:用匿名内部类的方式实现比较器Comparator接口中的compare()方法。适用于多个比较规则切换
Comparator
TreeSet<Student> ts = new TreeSet<Student>(new Comparator<Student>(){
//实现比较类的接口的匿名内部类,添加元素的时候,根据比较值看是否重复了
@Override
public int compare(Student s1,Student s2){
return 0;
}
});
Map集合
接口,(键,值),键不重复
采用多态方式创建对象。子类:hashMap
map.get(k)实现原理:先调用k的hashCode()方法得出哈希值,通过哈希算法转换成数组下标,通过下标定位,如果没有,返回null,如果有单项链表,那么会拿着k和单向链表的每个节点的k进行equals。
hash表数据结构
如果一个类得equals方法重写,那么他的hashCode方法必须重写。
为了提高检索效率,在JDK8之后,如果哈希表的单向链表中元素大于等于8个 并且 数组长度大于等于64时,单向链表这种数据结构才会变成红黑树数据结构,如果数组长度小于64则会先将数组进行一次扩容(2倍),如果扩容之后还没有达到长度64则继续比较下一个节点,链表不会转成红黑树,直到table数组长度大于等于64时单向链表才会变成红黑树,当红黑树上的节点数量小于6时,会重新把红黑树变成单向链表
put(),可以用来添加和修改值
remove(key)根据建删除元素
clear()移除所有
containKey()
containValue()
isEmpty()
size()
get(Key)根据建获得值
keySet()获取所有键的集合
values()获取所有值的集合
entrySet()获取所有键值对对象的集合
Map<String,String> map = new HashMap(String,String)();
map.put("2021","lilu");
map.put("2022","lili");
map.put("2021","lilii");//修改
Set<String> keySet = map.keySet();//ctl alt v
for (String key : keySet){
}
遍历map集合
Map<String,String> map = new HashMap(String,String)();
//遍历
Set<String> keySet = map.keySet();//ctl alt v
for (String key : keySet){
String value = map.get(key);
}
Set<Map.Entry<string,string>> entry = map.entrySet();//ctl alt v
for (Map.Entry<string,string> me: entry ){
String key = me.getKey();
String value = me.getValue();
}
HashMap
HashTable:线程安全
HashMap:线程不安全,效率高,允许null键和null值
Collections类
Collections是针对集合进行操作的工具类,其中的方法都是静态的
常用方法:
sort
bianrySort
max
min
shuffle随即转换
reverse
synchronizedList将线程不安全的List转换成线程安全的List
泛型
泛型是一种把数据类型明确的工作推迟到创建对象或者调用方法的时候才去明确的特殊的类型,也叫参数化类型,把类型当作参数一样传递
就是C++的模板
好处:1.把运行时期的问题提前到了编译期间;2、避免了强制类型转换
collection<String> c = new ArrayList<String>();
c.add("hello");
c.add("java");
c.add(100);//报错,泛型的好处
Iterator it = c.iterator();
while(it.hasNext()){
String s = (String)it.next();//ctl alt v,避免强转
System.out.println(s);
}
泛型类
格式:修饰符 class 类名<类型T>{}
public class A<T>{
private T t;
}
//使用:
A<String> a = new A<String>();
泛型方法
public class A<T>{
private T t;
public <T> show(T t){
//方法
}
}
//使用:
A<String> a = new A<String>();
泛型接口
public interface Generic<T>{
void show(T t);
}
//类实现泛型接口
public class A<T> implements Generic<T>{
public void show( T t){
}
}
类型通配符
List<?>:表示元素类型未知的List,元素可以匹配任何的类型,表示它是各种泛型List得父类,不能把元素添加到其中。
类型通配符上限:<?extends类型>
List<?extends Number>?:表示类型是Number或者其子类型。
类型通配符下限:<?super类型>
List<?super Number>?:表示类型是Number或者其父类型。
Collection<?> c1 = new ArrayList<Object>();
Collection<?> c2 = new ArrayList<Animal>();
Collection<?> c3 = new ArrayList<Dog>();
Collection<? extends Animal> c = new ArrayList<Dog>();//Dog继承类Animal,向下限定,后面new的类型必须是E及其子类
可变参数
public int sum(int b,int ...a){}把参数数据封装到数组,可以用迭代方式读出,包含多个参数时,可变参数放后面。
Arrays,List,Set接口中有静态方法of,返回包含任意个数元素得不可变得()
List<String>list = Arrays.asList("hello","world");
list.add("gg");//报错,不能添加
list.remove("hello");//报错,不能删除
list.set(1,"aa");//可以修改,因为不改变元素个数
List<String>list = List.of("hello","world")//接口的静态方法,直接调用
//list.add()/remove()/set()都不行
Set<String> set = Set.of("hello","world","java");//不能接受一样的参数,不支持add,remove,且set集合不含索引不能修改
多线程
线程:进程可进一步细化为线程,是一个程序内部的一条执行路径,同时它也是程序使用CPU的最基本单位(进程中要同时干几件事情,每一件事情的执行路径就是线程)
并行:多个CPU同时执行多个任务,可理解为多个人同时做不同的事
并发:多个进程实体同存于内存中
单线程:一个进程只用一条执行路径
多线程:一个进程有多条执行路径
不同的进程内存独立不共享,每一个线程都有自己的栈内存
java程序运行原理:Java命令会启动JVM,JVM可看作是一个应用程序,等同于启动了一个应用程序,也就是启动了一个进程,该进程会自动启动一个主线程,然后主线程会调用某个类的main方法,所以main方法运行在主线程中,然后再启动垃圾回收线程用来看护主线程,JVM至少启动了这两个线程,所以JVM是多线程的
多线程实现
线程启动后,只有run函数里的方法会被线程执行。如果直接调用run,是普通的调用。
start()方法的作用:启动一个分支线程,在JVM中开辟一个新的栈空间,只要新的栈空间开出来了,start()方法就结束了,启动成功的线程会自动调用run()方法,并且run()方法在分支栈的栈底部(压栈),这时main()方法在主栈的底部,所以run()方法与main()方法是平级的
//法1:编写一个类,直接继承Thread类并重写run()方法
public class lianxi_02
{
public static void main(String[] args)
{
//创建对象,该类继承了Thread类
MyThread mt = new MyThread();
mt.start();//启动线程
}
}
//自定义类继承Thread类,但是单继承,最好是法2,实现接口的形式
class MyThread extends Thread
{
//在自定义类中重写run()方法
public void run()
{
//被线程执行的代码
...
}
}
//法2常用,实现Runnable接口并重写run()方法。
public class lianxi_02
{
public static void main(String[] args)
{
//创建线程对象
MyThread mt = new MyThread();
//通过线程对象创建Thread对象
Thread t = new Thread(mt);
t.start();//启动线程
}
}
//自定义类实现Runnable接口
class MyThread implements Runnable
{
//在自定义类中重写run()方法
public void run()
{
//被线程执行的代码
}
}
方式二避免了Java单继承带来的局限性,适合多个相同程序的代码去处理同一个资源的情况,把线程同程序的代码、数据有效分离,较好的体现了面向对象的设计思想(低耦合、高内聚)
//法3:实现Callable接口(JDK8新特性
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;
/**
* 实现线程的第三种方式:实现Callable接口
*
* 这种方式的优点是:可以获取到线程的执行结果
* 这种方式的缺点是:在获取线程结果时容易引起当前线程阻塞,效率较低
*/
public class Lianxi_02
{
public static void main(String[] args)
{
MyThread1 mt = new MyThread1();
//创建一个“未来类”对象
FutureTask task = new FutureTask(mt);
//创建线程对象
Thread t = new Thread(task);
t.start();
try{
//通过get()方法获取线程返回值
System.out.println(task.get());
} catch (InterruptedException e){
e.printStackTrace();
} catch (ExecutionException e){
e.printStackTrace();
}
//get()方法会导致当前线程阻塞,所以此方法效率比较低
//因为get()方法需要等线程结束后拿到线程返回值
//所以main()方法这里的代码需要等get()方法结束才能执行,也就是要等以上线程结束后才执行
System.out.println("结束了");
}
}
//实现Callable接口
class MyThread1 implements Callable<String>
{
//这里的call()方法就相当于run()方法
public String call() throws Exception
{
String str = "hhhh";
Thread.sleep(5000);
System.out.println("2222");
Thread.sleep(5000);
System.out.println("3333");
return str;
}
}
Thread类常用函数
继承了Thread的类,可以直接.getName获取名字
1、返回当前正在执行的线程对象
public static Thread currentThread();
2、获取线程对象的名字
public final String getName();
3、更改线程对象的名字
public final void setName(String name);
4.得到当前正在运行的线程名字
Thread.currentThread().getName()
线程生命周期
新建、就绪、运行、阻塞、死亡
![](https://img-blog.csdnimg.cn/img_convert/4598bcd4a866ade12c52cfaef8d5bf36.png)
sleep静态函数,线程进入阻塞,出现在哪个线程中,就使哪个线程睡眠
interrupt方法:让当前线程出异常,从而达到中断线程的效果,可用它来唤醒睡眠的线程
public static void yield();暂停当前正在执行的线程对象,并执行其他线程,线程从“运行状态”回到“就绪状态”
结束线程:
public class ThreadTest
{
public static void main(String[] args)
{
MyThread2 mt = new MyThread2();
Thread th = new Thread(mt);
th.start();
//过5秒后
try{
Thread.sleep(5000);
} catch (InterruptedException e){
e.printStackTrace();
}
mt.run = false;//改变run属性,通过run的值来控制线程执行
System.out.println("终止成功");
}
}
class MyThread2 implements Runnable
{
// 打一个布尔标记
boolean run = true;
public void run()
{
for (int i = 0; i < 10; i++)
{
if (run)
{
System.out.println(Thread.currentThread().getName() + "---->" + i);
try{
Thread.sleep(1000);
} catch (InterruptedException e){
e.printStackTrace();
}
} else//如果run属性为false就结束run()方法,意味着线程结束了
{
return;
}
}
}
}
合并线程
public final void join();
在以下代码,合并线程的作用是:t1线程进入阻塞状态,t2线程执行,直到t2线程结束,t1线程才可以正常执行
public class Lianxi_02
{
public static void main(String[] args)
{
Thread t1 = new Thread(new Myth1());
t1.setName("t1");
t1.start();
}
}
class Myth1 implements Runnable
{
public void run()
{
Thread t2 = new Thread(new Myth2());
t2.setName("t2");
//启动t2线程
t2.start();
//合并t2
try{
t2.join();//t1进入阻塞,t2执行
} catch (InterruptedException e){
e.printStackTrace();
}
//t1的线程代码
for(int i = 0; i < 5; i++)
{
System.out.println(Thread.currentThread().getName() + "---->" + i);
}
}
}
class Myth2 implements Runnable
{
public void run()
{
//t2的线程代码
for(int i = 0; i < 5; i++)
{
System.out.println(Thread.currentThread().getName() + "---->" + i);
}
}
}
执行结果:
t2---->0
t2---->1
t2---->2
t2---->3
t2---->4
t1---->0
t1---->1
t1---->2
t1---->3
t1---->4
线程优先级
线程默认优先级是5
线程最高优先级是10
线程最低优先级是1
表示线程获取到CPU时间片的概率高
1、返回线程对象的优先级
public final int getPriority();
2、更改线程的优先级
public final void setPriority(int newPriority);
线程的调度模型
抢占式:枪CPU时间片,java就是这样
均分式:均分
线程安全
java提供了线程同步机制(synchronized)用于解决线程安全问题,其思想是:把多条语句操作共享数据的代码包装成一个整体,让某个线程在执行的时候,其他线程不能执行。实际就是线程不能并发了
1.synchronized代码块
法1:
synchronized(这里填的是想要同步的线程(也就是想要排队的线程)所共享的对象) {
//需要同步的代码块(这部分的代码块越少程序执行效率就越高)
}
eg:
//取款(操作同一账户)
public void Withdrawal(double money)
{
synchronized(this) {//这里的锁对象是账户对象,在这个类中就是this
//取之前的余额
double before = this.getMoney();
//取之后的余额
double after = before - money;
try{
Thread.sleep(1000);
} catch (InterruptedException e){
e.printStackTrace();
}
//设置取之后的余额
this.setMoney(after);
}
}
2.同步方法。在实例方法上使用synchronized,表示共享对象一定是this,且同步代码块是整个方法体(为了保护实例变量的安全),使用有局限性,但简化了代码
静态的同步方法的锁是锁类,所以需要锁的this变成:类名.class
//取款(操作同一账户)
public synchronized void Withdrawal(double money)
{
//取之前的余额
double before = this.getMoney();
//取之后的余额
double after = before - money;
try{
Thread.sleep(1000);
} catch (InterruptedException e){
e.printStackTrace();
}
//设置取之后的余额
this.setMoney(after);
}
3.在静态方法上使用synchronized,表示锁对象是类锁(字节码文件对象),类锁永远只有一把(为了保护静态变量的安全)
注意:
局部变量永远都不会存在线程安全问题,因为局部变量在栈中不共享,一个线程一个栈
实例变量在堆内存中,静态变量在方法区内存中,堆内存和方法区内存都是多线程共享的,所以可能存在线程安全问题
不要嵌套使用synchronized,可能导致死锁
解决线程安全的方案:
尽量使用局部变量代替实例变量和静态变量
如果必须是实例变量,那么可以考虑创建多个线程对象,一个线程一个对象,这样实例变量的内存就不共享了
如果不能使用局部变量,对象也不能创建多个,这个时候就只能选择synchronized同步机制了
Lock锁
接口,实现类:ReentrantLock来实例化
显示获取和释放锁,synchronized由JVM实现,不可中断,Lock由java代码实现,可中断
lock():获得锁
unlock():释放锁
public class Classx implements Runnale{
private Lock lock = new ReentrantLock();
@Override
public void run(){
while(true){
lock.lock();//上锁
try{if()}
finally{lock.unlock();}//释放锁
}
}
}
线程安全的类
StringBuffer.源码都加了同步代码块,但是通常用StringBuider,因为它不执同步
Vector,不需要线程安全,可以用ArrayList
HashTable不需要线程安全,可以用HashMap
List = Collection.synchronizedList(new ArrayList<>());此函式使得线程不安全的ArrayList变成线程安全
守护线程
用户线程(如主线程main方法)
守护线程(后台线程,如垃圾回收线程)一般的守护线程是一个死循环,所有的用户线程只要结束,守护线程就自动结束
定时器
间隔特定的时间,执行特定的程序
wait() notify()
Object类自带的方法。
wait()让对象上活动的线程进入等待状态,并释放对象锁
notify():唤醒对象上等待的单个线程(若有多个线程等待,则随机选择一个线程唤醒),不释放锁
notifyAll():唤醒对象上等待的所有线程,不释放锁.
sleep()只能通过线程对象调用,且必须指定睡眠时间,不释放锁。wait()方法可以通过任意对象调用,且可指定时间,也可不指定时间,释放锁
获取文件绝对路径
前提是文件必须在当前类路径下(在当前src下),这种写法无论在哪个系统上都可获得绝对路径,是通用的
String path = Thread.currentThread().getContextClassLoader()
.getResource("从当前类路径开始的文件路径(相对路径)").getPath();
//Thread.currentThread();表示获取当前线程对象
//getContextClassLoader();表示获取当前线程的类加载器对象
//getResource();获取资源,当前线程的类加载器默认从类的根路径下加载资源
直接返回流
InputStream path = Thread.currentThread().getContextClassLoader()
.getResourceAsStream("从当前类路径开始的文件路径(相对路径)");
资源绑定器
便于获取属性配置文件(.properties)中的内容
网络编程
IP,端口标识应用程序,协议:UDP,TCP
IP地址
ipconfig ping
127.0.0.1 回送地址,可以代表本机,用于测试
InetAddress类
表示ip地址的对象,没有构造方法。
方法:
getByName(String host);静态方法,确定主机的IP地址,主机名可以是机器名称/IP地址
getHostName()获取此IP地址的主机名
getHostAddress()返回文本显示中的IP地址字符串。
InetAddress address = InerAddress.getByName("主机名/ip地址");#返回一个InetAddress对象
String name = address.getHostName();#获取主机名
String ip = address.getHostAddress();#获取ip地址
端口和协议
设备上应用程序的唯一标识。端口号:0~65535,普通应用程序使用1024以上的
UDP协议:无连接的通信协议,发送端不确认接收端是否存在。接收端接收后也不会反馈。消费资源小,效率高,常用于音频、视频。
TCP协议:面向连接的,可靠无差错的。发送端和接收端先建立逻辑链接再传数据,每次连接创建需要经过三次握手。
三次握手:
客户端发送连接请求,等待服务器确认
服务端回送响应,通知收到了请求
客户端再次发送确认。
UDP
通信两端各建立一个Socket对象,但是Socket只发送,接受。通信双方没有客户端和服务器感念
DatagramSocket类作为UDP协议的Socket
UDP发送数据的步骤
创建发送端Socket对象(DatagramSocket类)
打包数据 DatagramPacket类
调用DatagramSocket对象的发送方法,发送DatagramPacket类 对象
关闭发送端
DatagramSocket ds = new DatagramSocket();
byte[] bys = "hhh".getBytes();
int length = bys.length;
InetAddress address = InerAddress.getByName("主机名/ip地址");
int port = 1000;
DatagramPacket dp = new DatagramPacket(bys,length,address,port);
ds.send(dp);
ds.close()
UDP接收数据
创建接受端Socket对象(DatagramSocket类)
创建数据包,用于接收
调用DatagramSocket对象的接受方法需要DatagramPacket类 对象
解析数据包
DatagramSocket ds = new DatagramSocket(1000);//指定端口的初始
byte[] bys = new byte[1024];
DatagramPacket dp = new DatagramPacket(bys,bys.length);
ds.receive(dp);//接收数据
//解析数据
byte[] datas = dp.getData();
String dataString = new String(datas);
datas....
ds.close();
TCP
发送数据
Socket s = new Scoket(InerAddress.getByName("192.168.1.66"),10000);
Socket s = new Scoket("192.168.1.66",10000);
//获取输出流写数据
OutputStream os = s.getOutputStream();
os.write("hhh".getBytes());
s.close();
接收数据
创建服务器端Socket对象(ServerSocket类)
获取输入流,读数据
释放资源
ServerSocket ss = new ServerSocket(1000);
//accept 侦听要连接到此套接字并接受它
Socket s = ss.accept();
//获取输入流,读数据
InputStream is = s.getInputStream();
byte[] bys = new byte[1024];
int len = is.read(bys);
String data = new String (bys,0,len);
s.close();
ss.close();
lambda表达式
允许把函数作为一个方法的参数(函数作为参数传递进方法中) 闭包
//创建线程对象
MyThread mt = new MyThread();
//通过线程对象创建Thread对象
Thread t = new Thread(mt);
t.start();//启动线程
//使用匿名内部类简化代码--------------
new Thread(new Runnable(){
public void run(){
sss}
}).start();
//使用lambda表达式简化代码----------不需要定义类了
new Thread(()->{
ssss
}).start();
格式:(形式参数)-> {代码块}
使用前提:有接口,且接口中只有一个抽象方法
接口作为形式参数传播的时候,需要采用多态的方式,就需要定义一个实现接口的类,麻烦,建议用lambda表达式
//带参数
//使用匿名内部类简化代码--------------
new Thread(new Runnable(){
@Override
public void run(String s){
sss;
}
}).start();
//使用lambda表达式简化代码----------不需要定义类了
new Thread((String s)->{
ssss
}).start();
省略
//1.省略类型,但是有多个参数的时候,不能只省略一个
useFunc((x,int y)->{
return x+y;
});
//2参数只有一个,可以省小括号
useFunc(x->{
return x;
});
//3代码块语句只有一条,可以省略大括号和分号,return
useFunc(x-> out(x);)
和匿名内部类的区别
lambda表达式必须是接口。
匿名内部类:编译之后产生一个单独的.class字节文件
lambda:编译之后,没有一个单独的.class字节码文件,对应的字节码会在运行时动态生成
反射
类加载
类加载:类初始化。程序要使用某个类,如果这个类还没有加载到内存,系统会通过类的加载,类得连接,类得初始化三个步骤进行初始化。JVM完成
类的加载:将class文件读入内存,并为之创建一个java.lang.class对象。
类的连接:
类的初始化:创建类对象、调用类方法,访问类变量、使用反射方式强制创建某个类/接口对应的Class对象、初始某个类的子类、直接使用java。exe运行主类
类加载器
将class文件读入内存,并为之创建一个java.lang.class对象。
JVM的类加载机制:
全盘负责:当一个类加载器负责加载某个类时,该类所依赖的和引用的其他类也将由该类加载器负责载入,除非显示的使用另一个类加载器来载入。
父类委托:当一个类加载器负责加载某个类时,先让父类加载器加载,不行就从类路径中加载
缓存机制:保证所有加载过的class都会被缓存,当程序需要某个class对象时,类加载器先从缓存区中搜索该class,只有当缓存区中不存在该class对象时,系统才会读取该类对应的二进制的数据,并将其转成class对象,存储到缓存区。
ClassLoader类
Bootstarp类
Bootstarp->PlatformClassLoader->AppCLassLoader
反射
反射就是通过字节码文件对象把java类中的各种成分(变量、方法、构造方法)映射成一个个java对象,实际上是通过class对象反向获取该类的信息,即通过类的字节码文件动态的解析类
类通过类加载器加载到内存,得到这个类的一个影像(成员变量,构造方法,成员方法。。),这个印象用类Class包装,可以用CLass直接使用该类的东西
获取class对象
获取Class对象的三种方式(在运行期间,一个类只有一个class对象产生)
方式一:使用CLass的静态方法获取,会导致类加载,类加载静态代码块执行
Class<?> c = Class.forName("带有包名的完整类名");
方式二:会执行静态代码块
Class<? extends 类> c = 对象.getClass();
方式三:java语言中任何一种类型,包括基本数据类型都有.class属性 不会执行静态代码块(不会静态初始化)
Class c = 任何类型.class;
反射获取构造方法
java.lang.reflect.Constructor<T>
获取批量的构造方法对象
public Constructor<?>[] getConstructors();//所有公有的构造方法
public Constructor<?>[] getDeclaredConstructors();//获取所有的构造方法(包括私有、受保护、默认、公有)
获取单个指定的构造方法对象
//Class<?>... parameterTypes表示参数类型的字节码,如String.class、int.class
public Constructor<T> getConstructor(Class<?>... parameterTypes);//获取单个指定的公有构造方法
public Constructor<T> getDeclaredConstructor(Class<?>... parameterTypes);//获取单个指定不限修饰符的构造方法
创建新对象
public T newInstance(Object... initargs);//括号里的是填实际参数
反射获取成员变量对象
java.lang.reflect.Field
获取批量的成员变量对象
public Field[] getFields();//获取公有的成员变量
public Field[] getDeclaredFields();//获取所有成员变量,包括:私有、受保护、默认、公有
通过变量名获取单个指定的成员变量对象
public Field getField(String name);//获取指定公有的成员变量
public Field getDeclaredField(String name);//获取单个指定成员变量、不限修饰符
获取变量的值,如果是私有就需要关闭安全检查
public Object get(Object obj);//括号里表示对象,返回该对象的某个变量的值
给获取的变量赋值
public void set(Object obj, Object value);//obj表示变量所在的对象,value表示要为变量赋的值
反射获取成员方法对象
java.lang.reflect.Method
获取批量的成员方法对象
public Method[] getMethods();//获取公有的成员方法,包含了父类的公有成员方法
public Method[] getDeclaredMethods();//获取所有的成员方法,包含了私有成员方法,不包括继承的
获取单个指定成员方法对象
public Method getMethod(String name, Class<?>... parameterTypes);//获取公有的指定成员方法,name是方法名,后面填的是形参类型字节码
public Method getDeclaredMethod(String name, Class<?>... parameterTypes);//获取所有的成员方法
调用该成员方法
public Object invoke(Object obj, Object... args);//obj表示要调用方法的对象,args表示调用方法时传递的实际参数
获取类的父类对象
public Class<? super T> getSuperclass();
获取类实现的所有接口对象
public Class<?>[] getInterfaces();
反射的核心是:JVM 在运行时才动态加载类或调用方法/访问属性,它不需要事先(写代码的时候或编译期)知道运行对象是谁