静态方法的形参,静态变量,静态代码块中不能使用泛型(因为即使泛型不同,还是同一个类,静态变量是属于类的)
instanceof运算符后不能跟泛型类:a instanceof ArrayList
A extends B A[]是B[]的子类 但是List 不是List
的子类
通配符的使用
public static void main(String[] args) {
List<Integer> integers = new ArrayList<>();
//f1(integers); 不能调用 List 不是 List
f2(integers);//可以使用通配符调用
}
public static void f1(List<Object> list){
for (Object o : list) {
System.out.println(o);
}
list.add("123");
list.add(123);
}
public static void f2(List<?> list){
for (Object o : list) {
System.out.println(o);
}
//不能添加元素 因为不知道?是什么类型
//list.add("123"); 如果?是Integer类型呢
// list.add(123); 如果?是String类型呢
//只能添加null
list.add(null);//可以添加null 因为null是所有类的子类
}
class A{
}
class A1 extends A{
}
class A2 extends A{
}
public static void main(String[] args) {
List<A> list1=new ArrayList<>();
f1(list1);//ok
f2(list1);//ok
f3(list1);//ok
List<A2> list2=new ArrayList<>();
f1(list2);//ok
f2(list2);//ok
f3(list2);//not ok A2不是A1的子类
}
//f1 f2 f3不构成参数重载 函数名必须不同
public static void f1(List<?> list){
}
public static void f2(List<? extends A> list){
}
public static void f3(List<? super A1> list){
}
上下限的设置不仅仅可以配合?一起使用,也可以单独作为类的泛型界限
class A <T extends Number>{
T a;
}
关于泛型类(接口的继承)
class A <T>{
}
class A1 extends A<String>{
}
class A2<T> extends A{//A2是子类中的新的泛型 与A无关 A中的泛型默认是Object
}
class A3<T> extends A<T>{//A2中的T和A相同 继承了T
}
class A4<T,E> extends A<T>{//继承了T并且拓展了泛型E
}
//class A5 extends A{
//}
//class A6 extends A{
//}
泛型方法
public static void main(String[] args) {
Integer f = f(new ArrayList<Integer>(), 12);
Double f1 = f(new ArrayList<String>(), 1.2);
}
public static <T,E> E f(List<T> list,E value){
return value;
}
//泛型方法中的形参也可以和?一起使用
public static <T> void f2(List<? extends T> list){
}
public static void main(String[] args) {
ArrayList<Integer> src = new ArrayList<>();
src.add(1);
src.add(2);
ArrayList<Integer> dest = new ArrayList<>();
copy(src,dest);
}
//参数互相依赖
public static <T> void copy(List<T> src,List<T> dest){
for (int i = 0; i < src.size(); i++) {
dest.add(src.get(i));
}
}
public static void f2(List<?> src,List<?> dest){
for (int i = 0; i < src.size(); i++) {
// dest.add(src.get(i)); dest的类型是? 只能往里面添加null
}
}
//参数与返回值依赖
public static <T> T f(List<T> list){
return list.get(0);
}
构造器泛型和菱形语法失效
class A {
public <T> A(T x){
System.out.println(x);
}
}
public static void main(String[] args) {
//构造器泛型
new A(123);
new A("1.23");
}
class A<E> {
public <T> A(T x){
System.out.println(x);
}
}
public static void main(String[] args) {
A<String> a1=new A<>(123);
// A a2=new A(123); 显示指定了泛型构造器中的形参类型
}
泛型擦除
public static void main(String[] args) {
List<Integer> list1=new ArrayList<>();
list1.add("123");//not ok
List list2=list1;//泛型擦除
list2.add("123");// ok
List list3=new ArrayList();//不带泛型的对象赋值给一个带泛型的变量-->"堆污染"
List<Integer> list4=list3;//list4只能添加Integer元素
}
没有泛型数组
//java数组存在型变的问题:A是B的子类 A[]也是B[]的子类 可以向上转型
Integer[] a=new Integer[2];
Object[] b=a;
b[0]="hello";//编译ok 运行期间错误
//由于上面的型变问题,假设可以创建泛型数组
List<Integer>[] a=new ArrayList<Integer>[2];//编译ok(假设可以创建泛型数组)
Object[] b=a;//ok
a[1]=new ArrayList<String>();//运行期间错误
//所以不能创建泛型数组
//此外
T a=new T[2];//这种方式也不行
//数组的创建在运行期间,泛型信息在运行期间没有,所以不知道创建什么类型的数组
//ps:这样的创建数组是可以的 即不给ArrayList添加泛型
List<Integer>[] a=new ArrayList[2];
a[0]=new ArrayList<Integer>();
a[1]=new ArrayList<Integer>();
//a[1]=new ArrayList(); not ok
a[1].add("123");//not ok 引用类型的泛型限制
a[1].add(123);//ok
//或者
List[] a=new ArrayList[2];
a[0]=new ArrayList<Integer>();
a[0].add("123");//上面的Integer泛型失效 因为引用类型的泛型是Object 所以可以添加"123"
a[0].add(123);
a[1]=new ArrayList<String>();
a[1].add("123");//ok
a[1].add(123);//ok
Scanner可以读键盘输入、也可以读取文件
scanner.useDelimiter("\n");
Scanner scanner=new Scanner(System.in);
Scanner scanner = new Scanner(new File("data.txt"));
System类
public static void main(String[] args) throws FileNotFoundException {
/**
* getenv:获取系统变量
* getProperties: 获取系统属性值
*/
System.out.println(System.getenv("JAVA_HOME"));
System.out.println(System.getProperty("os.name"));
Map<String, String> env = System.getenv();//获取所有环境变量
for (String s : env.keySet()) {
System.out.println(s+"--->"+env.get(s));
}
Properties properties = System.getProperties();//获取系统所有的属性值
System.out.println(properties);
/**
* 获取毫秒和纳秒时间戳
*/
System.out.println(System.currentTimeMillis());//获取当前时间戳ms
System.out.println(System.nanoTime());//获取当前时间戳ns
/**
* identityHashCode:唯一标识对象
* 值相同则说明两个对象的地址相同 是同一个对象
*/
Object o1 = new Object();
Object o2 = new Object();
System.out.println(System.identityHashCode(o1));//460141958
System.out.println(System.identityHashCode(o2));//460141958
String s1=new String("hello");
String s2=new String("hello");
System.out.println(System.identityHashCode(s1));//460141958
System.out.println(System.identityHashCode(s2));//356573597
String s3="hello";
String s4="hello";
System.out.println(System.identityHashCode(s3));//1735600054
System.out.println(System.identityHashCode(s4));//1735600054
}
Runtime类
public static void main(String[] args) throws IOException {
Runtime runtime = Runtime.getRuntime();
System.out.println(runtime.availableProcessors());//可用cpu核数
System.out.println(runtime.freeMemory());//空闲jvm内存 单位字节
System.out.println(runtime.maxMemory());//最大jvm内存
runtime.exec("notepad.exe");//打开记事本
}
Object类
class Person implements Cloneable{
String name;
Pet pet;
@Override
protected Person clone() throws CloneNotSupportedException {
return (Person) super.clone();
}
public Person(String name, Pet pet) {
this.name = name;
this.pet = pet;
}
}
class Pet{
String name;
public Pet(String name) {
this.name = name;
}
}
public static void main(String[] args) throws IOException, CloneNotSupportedException {
Person p1 = new Person("tom",new Pet("dog"));
Person p2=p1.clone();
/**
* clone是浅拷贝
* Pet这个引用类型的变量只是拷贝了引用
*/
System.out.println(p1==p2);//false
System.out.println(p1.pet==p2.pet);//true
}
//如何修改为深拷贝?
//1. 给Pet类也实现 clone()方法
@Override
protected Pet clone() throws CloneNotSupportedException {
return (Pet) super.clone();
}
//2. 修改Person类的clone方法如下
@Override
protected Person clone() throws CloneNotSupportedException {
Person p=(Person) super.clone();
p.pet=pet.clone();
return p;
}
集合转化为数组
Integer[] arr= new Integer[list.size()];//不能是int[] 必须是Integer[]
arr=list.toArray(arr);
System.out.println(Arrays.toString(arr));
Object[] arr2=list.toArray();//这种方式只能返回Object[]类型的数组
System.out.println(Arrays.toString(arr2));
集合框架
Collection接口
Map接口
Lambda表达式遍历集合
list.forEach(new Consumer<Integer>() {
@Override
public void accept(Integer integer) {
System.out.print(integer);//lambda的本质:匿名内部类
}
});
list.forEach(x->System.out.println(x));//可以添加{}进行其他操作
list.forEach(System.out::println);//只能单纯打印 比较方便
使用Iterator遍历集合
HashSet(HashMap)添加元素过程
class A{
int a;
@Override
public int hashCode() {
return this.a;
}
@Override
public boolean equals(Object obj) {
A x=(A)obj;
return this.a==x.a;
}
public A(int a) {
this.a = a;
}
}
public static void main(String[] args) {
A x= new A(123);
HashSet<A> set=new HashSet<>();
set.add(x);
System.out.println(set.size());
x.a=456;
System.out.println(set.remove(new A(456)));//false 删除失败 因为x在set中的位置是123 位置456是空着的
set.add(new A(123));//添加成功 该对象会落到123位置 该位置已经有x了 然后使用equals比较 由于x的a属性值被修改了 比较失败 equals返回false 因此添加成功
System.out.println(set.size()); // 2
}
TreeSet注意事项
TreeSet<Integer> treeSet=new TreeSet<>();
treeSet.add(3);
treeSet.add(1);
treeSet.add(2);
System.out.println(treeSet);//1 2 3
TreeSet<Integer> treeSet2=new TreeSet<>((a,b)->b-a);//lambda简化了new Comparator
treeSet2.add(3);
treeSet2.add(1);
treeSet2.add(2);
System.out.println(treeSet2);//3 2 1
List接口注意事项
class A{
int num;
public A(int num) {
this.num = num;
}
@Override
public boolean equals(Object obj) {
return true;
}
}
public static void main(String[] args) {
List<A> list=new ArrayList<>();
A a1 = new A(123);
A a2 = new A(456);
list.add(a1);
list.add(a2);
System.out.println(list.indexOf(a2));//0
list.remove(a2);//看似删除a2实际上把a1删除了
System.out.println(list.get(0).num);//456
}
Arrays工具类
List<Integer> numbers = Arrays.asList(1, 2, 3, 4);
System.out.println(numbers);
System.out.println(numbers.getClass());//class java.util.Arrays$ArrayList
numbers.add(1);//报错
numbers.remove(1);//报错
public static void sort(int[] a)
public static int binarySearch(int[] a, int key)
public static void fill(int[] a, int val)
public static int[] copyOf(int[] original, int newLength)
HashMap
Properties的使用
public static void main(String[] args) throws IOException {
Properties properties = new Properties();
properties.setProperty("name","root");
properties.setProperty("passwd","123");
//写到文件中
properties.store(new FileOutputStream("info.properties"),"user info");
Properties properties1 = new Properties();
//从properties文件中读取
properties1.load(new FileInputStream("info.properties"));
System.out.println(properties1);//{passwd=123, name=root}
System.out.println(properties1.getProperty("name"));//root
}
WeakHashMap
public static void main(String[] args) throws IOException {
WeakHashMap<Object, String> weakHashMap = new WeakHashMap<>();
weakHashMap.put(new Object(), "ob1");
weakHashMap.put(new Object(), "ob2");
weakHashMap.put(new Object(), "ob3");
System.out.println(weakHashMap.size());//3
System.gc();
System.runFinalization();
System.out.println(weakHashMap.size());//0
}
//map中的key实际上是对象的引用 当发生gc后 三个obj对象被回收 之后weakHashMap会删除这三个对象对应的引用变量key-value对
Collections工具类拥有的功能
无论使没使用try…catch,JVM都会产生一个异常对象,如果处理了,程序正常退出(Process finished with exit code 0),否则程序异常退出(Process finished with exit code 1)
try后面的catch捕获的范围应该从小到大,从大到小会编译异常,cathch块只会执行一个
try后面至少需要一个catch块或者finally块
一个catch块中可以有多个异常
try {
int a = 1 / 0;
} catch (ArithmeticException | IndexOutOfBoundsException e) {
e.printStackTrace();
}
Throable(分类和继承体系)
fianlly块
private static void f() {
try {
int a = 1 / 1;
return;
} catch (ArithmeticException e) {
e.printStackTrace();
}finally {
System.out.println("finally");
}
}
/**
* finally块中对a的累加赋值不会影响最终的返回值1
* 因为在return a时a的值被保存了一份到栈中
* @return 1
*/
private static int f() {
int a=1;
try {
return a;
} catch (ArithmeticException e) {
e.printStackTrace();
}finally {
a=5;
a++;
}
return -1;
}
/**
* 在finally块中直接返回了
* @return 6
*/
private static int f() {
int a=1;
try {
return a;
} catch (ArithmeticException e) {
e.printStackTrace();
}finally {
a=5;
a++;
return a;
}
}
运行时异常和编译时异常
Throws和Throw
try {
int i=1/0;
}catch (ArithmeticException e){
throw new RuntimeException("除数不能为0");
}
子类重写父类的方法时,子类重写的方法的异常<=父类中的异常
自定义异常类
class MyException extends Exception{
public MyException() {
}
public MyException(String message) {
super(message);
System.out.println("自定义异常");
}
}
try {
int i=1/0;
}catch (ArithmeticException e){
throw new MyException("除数不能为0");
}
自定义注解使用@interface, 注解中的成员变量使用无参方法声明
public @interface MyTag {
String name() default "tom";
int age() default 18;
}
public class Demo1 {
@MyTag
public void f(){
}
}
注解不会自己生效,需要开发者处理
@Retention(value = RetentionPolicy.RUNTIME)//声明注解可以保留到运行时
public @interface MyTag {
String name() default "tom";
int age() default 18;
}
class A{
public void f(){}
}
class A1 extends A{
@MyTag(name = "bob",age = 19)
@Override
public void f() {
super.f();
}
}
public static void main(String[] args) throws ClassNotFoundException, NoSuchMethodException {
Annotation[] annotations = Class.forName("annotation.A1").getMethod("f").getAnnotations();
System.out.println(annotations.length);//1 @Override注解在运行时获取不到
for (Annotation annotation : annotations) {
if(annotation instanceof MyTag){
System.out.println("tag.name="+((MyTag) annotation).name());
System.out.println("tag.age="+((MyTag) annotation).age());
}
}
}
3种注解范围
@Retention(RetentionPolicy.SOURCE)
@Retention(RetentionPolicy.CLASS)
: 默认,不能通过发射获取@Retention(RetentionPolicy.RUNTIME)
,保留到运行时,可以通过反射获取@Target(value = ElementType.FIELD)
用来指明注解作用范围,作用于类、方法、属性…
自定义一个类似于Junit中的@Test注解
public class Demo1 {
@MyTest
public void f1(){
System.out.println("f1");
}
@MyTest
public void f2(){
System.out.println("f2");
}
@MyTest
public void f3(){
System.out.println("f3");
}
@MyTest
public void f4(){
System.out.println("f4");
}
public static void main(String[] args) throws ClassNotFoundException, InvocationTargetException, IllegalAccessException {
Method[] methods = Class.forName("annotation.Demo1").getMethods();
Demo1 obj = new Demo1();
for (Method method : methods) {
if(method.isAnnotationPresent(MyTest.class)){
method.invoke(obj);
}
}
}
}
@Retention(value = RetentionPolicy.RUNTIME)
@Target(value = ElementType.METHOD)
public @interface MyTest {
}
JBDC提供了一套接口,底层实现可以是mysql的驱动jar包,也可以是oracle的驱动jar包(这些jar包由数据库产商提供实现)
数据库语言分类
JDBC常用API
JDBC编程步骤
Class.forName("com.mysql.jdbc.Driver")
,最新的数据库驱动不需要这一步public static void main(String[] args) throws ClassNotFoundException {
Class.forName("com.mysql.cj.jdbc.Driver");//加载数据库驱动 高版本mysql.jar包可以省略这一步
String url="jdbc:mysql://localhost:3306/test?useSSL=true&serverTimezone=Asia/Shanghai";
String userName="root";
String password="123";
try (//自动关闭资源的try用法
//获取数据库连接
Connection connection= DriverManager.getConnection(url,userName,password);
Statement statement = connection.createStatement();
ResultSet rs = statement.executeQuery("select * from student");
){
while(rs.next()){
System.out.println("stuNo: "+rs.getInt("stuno")+" name: "+rs.getString("name"));
}
} catch (SQLException e) {
e.printStackTrace();
}
}
execute
Connection connection= DriverManager.getConnection(url,userName,password);
Statement statement = connection.createStatement();
boolean hasResult = statement.execute("select * from student");
if(hasResult){
ResultSet resultSet = statement.getResultSet();
while(resultSet.next()){
System.out.println(resultSet.getInt("stuno")+" "+resultSet.getString("name"));
System.out.println(resultSet.getInt(1)+" "+resultSet.getString(2));
}
}
PreparedStatement
Connection connection= DriverManager.getConnection(url,userName,password);
//由于主键自增 所以第一个参数可以设置为null
PreparedStatement preparedStatement = connection.prepareStatement("insert into student values(null,?) ");
for (int i = 0; i < 10; i++) {
preparedStatement.setString(1,"Tim"+i);
preparedStatement.execute();
}
Blob: 存储二进制数据到数据库(图片 视频等)
//存储图片数据
Connection connection= DriverManager.getConnection(url,userName,password);
PreparedStatement preparedStatement = connection.prepareStatement("insert into student values(null,?,?) ");
for (int i = 0; i < 5; i++) {
FileInputStream fileInputStream = new FileInputStream("1.jpg");
preparedStatement.setString(1,"Tim"+i);
preparedStatement.setBinaryStream(2,fileInputStream,fileInputStream.available());
preparedStatement.execute();
}
//读取图片数据
ResultSet resultSet = statement.executeQuery("select * from student");
while (resultSet.next()){
String name=resultSet.getString(2);
Blob blob = resultSet.getBlob(3);//数据库表第3列村存储的是图片
FileOutputStream out = new FileOutputStream(name+".jpg");
out.write(blob.getBytes(1, (int) blob.length()));
}
JDBC默认事务自动提交
connection.setAutoCommit(false);
: 开启事务,需要手动提交connection.commit();
: 手动提交事务connection.rollback();
: 回滚事务批量操作
Statement statement = connection.createStatement();
//只能添加DDL DML语句 不能执行查询语句
statement.addBatch("update student set name='tom111' where stuno=1");
statement.addBatch("update student set name='tom222' where stuno=2");
statement.addBatch("update student set name='tom333' where stuno=3");
int[] ret = statement.executeBatch();//每个sql语句执行结果的返回值
File类
输入/输出流
流的分类
InputStream和Reader(两个都是抽象类)
FileInputStream和FileReader
/**
* FileInputStream
* 逐个字节读取 当有汉字时会出现乱码
*/
FileInputStream inputStream = new FileInputStream("a.txt");
int b=0;
while((b=inputStream.read())!=-1){
System.out.println((char)b);
}
/**
* FileInputStream
* 使用字节数组读取
* 也会出现中文乱码
* 假设文件内容:中国
* 字节数组b为3时可以读取正确(utf-8汉字3个字节编码)
* 字节数组b为4时就会乱码
*/
FileInputStream inputStream = new FileInputStream("a.txt");
byte[] b=new byte[3];
int len;
while((len=inputStream.read(b))!=-1){
System.out.println(new String(b,0,len));
}
/**
* FileReader
* 逐个字符读取 不会发生中文乱码
*/
FileReader reader = new FileReader("a.txt");
int c=-1;
while((c= reader.read())>0){
System.out.println((char)c);
}
/**
* FileReader
* 用字符数组读取
*/
FileReader reader = new FileReader("a.txt");
char[] buf=new char[4];
int len=-1;
while((len= reader.read(buf))>0){
System.out.println(new String(buf,0,len));
}
关于Unicode字符集和编码格式的一些思考
Unicode给各国语言字符都设置了一个编号,比如汉字’中’的编号是20013,而UTF-8和GBK是编码格式,举个例子,8可以怎么表示,可以是0001000一个字节表示,也可以是0000000,0001000两个字节表示,不管你怎么表示,只要能最终根据表示方式还原成8即可;因此不管使用utf-8编码也好、gbk编码也好,只是把字符用不同的形式编码而已,码点值都是一样的,那么在解码时就可以安装相同的编码方式解码,得到正确的码点值,这样就不会产生乱码
String s="你好";
System.out.println(s.getBytes("utf-8").length);//6 u8采用3个字节表示一个汉字
System.out.println(s.getBytes("gbk").length);//4 gbk采用2个字节表示一个汉字
//将一个utf-8编码的字符串转化成gbk编码的字符串
String s_utf8="你好";
byte[] bytes = s_utf8.getBytes("gbk");//先以gbk方式获取字节
String s_gbk=new String(bytes,"gbk");//再以gbk方式创建字符串
System.out.println(s_gbk);
//所以字节是通用的 这也就是为什么后面有转化流的出现
//假如一个文件的编码格式是gbk 平台系统的编码方式是utf-8 则可以先使用字节流以gbk的方式读取,然后转化成utf-8格式的字符输入流
//假如当前平台系统的编码方式是utf-8,需要将数据以gbk格式存储,则可以将字符数据以utf-8方式获取字节输出流,然后再转化成gbk格式的字符输出流
OutputStream和Writer
OutputStream中的方法
Writer中的方法
FileOutputStream和Writer
FileOutputStream outputStream = new FileOutputStream("out.txt");
int b=67;
outputStream.write(b);
byte[] bytes={'a','b','c'};
outputStream.write(bytes);
outputStream.write(bytes,1,2);
outputStream.flush();
outputStream.close();
//文件内容:Cabcbc
FileWriter writer = new FileWriter("out.txt");
char c='a';
char[] buf={'b','c','d'};
String str="efg";
writer.write(c);
writer.write(buf);
writer.write(buf,1,2);
writer.write(str);
writer.write(str,1,2);
writer.flush();//不要忘记刷新
writer.close();//不要忘记关闭流 一般关闭时会自动刷新
处理流(PrinsStream)用来包装节点流(FileInputStream FileOutputStream FileReader FileWriter)
//处理流1PrintStream
FileOutputStream outputStream = new FileOutputStream("out.txt");
PrintStream printStream = new PrintStream(outputStream);
printStream.print("123");//没有换行符
printStream.println("hello world");//自动写入换行符
printStream.print("456");//没有换行符
printStream.close();//可以只关闭最上层处理流 被包装的节点流会被自动关闭
outputStream.close();
转化流
InputStreamReader: 字符输入流->字节输入流
OututStreamReader: 字符输出流->字符输出流
ps: 没有字符流->字节流,因为没有必要,一是字符流使用起来更方便,二是字节流可以转化为字符流
InputStreamReader使用场景:
//out.txt是gbk编码的 内容:中国是一个伟大的国家!
FileReader reader = new FileReader("out.txt");
char[] buf=new char[1024];
int len=-1;
while((len=reader.read(buf))>0){
System.out.println(new String(buf,0,len));
}
//输出:�й���һ��ΰ��Ĺ���! 因为机器是utf-8去解码的 所以乱码了
//解决:使用InputStreamReader转化
FileInputStream inputStream = new FileInputStream("out.txt");//字节读取是不管文件编码方式的
InputStreamReader reader = new InputStreamReader(inputStream,"gbk");//用gbk方式解码得到字符输出流
char[] buf=new char[1024];
int len=-1;
while((len=reader.read(buf))>0){
System.out.println(new String(buf,0,len));
}
OutputStreamReader使用场景:
//当前系统编码是utf-8 需要将"中国是一个伟大的国家!"以gbk的编码格式写出
FileOutputStream outputStream = new FileOutputStream("out.txt");
OutputStreamWriter writer = new OutputStreamWriter(outputStream,"gbk");
writer.write("中国是一个伟大的国家!");
writer.close();
重定向输出
FileOutputStream outputStream = new FileOutputStream("out.txt");
PrintStream printStream = new PrintStream(outputStream);
System.setOut(printStream);
System.out.println("hello world");
System.out.println("你好 世界");//本来应该打印到控制台 现在重定向到了文件中
printStream.close();
重定向输入
FileInputStream inputStream = new FileInputStream("out.txt");
System.setIn(inputStream);
Scanner scanner = new Scanner(System.in);
while (scanner.hasNextLine()) {//无需键盘输入 直接从out.txt文件中读取数据
System.out.println("键盘输入的内容: "+scanner.nextLine());
}
对象流以及序列化反序列化
//序列化
FileOutputStream outputStream = new FileOutputStream("obj.txt");
ObjectOutputStream stream = new ObjectOutputStream(outputStream);
Person p = new Person("tom", 18);
stream.writeObject(p);
stream.close();
//反序列化
FileInputStream inputStream = new FileInputStream("obj.txt");
ObjectInputStream objectInputStream = new ObjectInputStream(inputStream);
Person p = (Person) objectInputStream.readObject();
System.out.println("name: "+p.name+" age: "+p.age);
objectInputStream.close();
//序列化反序列化的对象需要实现Serializable(空接口)
class Person implements Serializable{
public String name;
public int age;
public Person(String name, int age) {
this.name = name;
this.age = age;
}
}
//如果序列化之后又修改了Person类就会反序列化失败
//添加序列化号的作用:
//private static final long serialVersionUID = 1; 名字必须是serialVersionUID
//序列号不变,即使修改了Person类也可以反序列化
//序列号改变,即使没有修改Person类反序列化也会失败
//总之,序列化和反序列化时的serialVersionUID应该是一样的 否则会反序列化失败
反序列化时不会调用构造器创建对象
对某个成员属性如果不想被序列化,可以使用transient修饰
静态变量不会被序列化
带有引用属性的序列化
//序列化
FileOutputStream outputStream = new FileOutputStream("obj.txt");
ObjectOutputStream stream = new ObjectOutputStream(outputStream);
Pet dog = new Pet("dog");
Person p1 = new Person("tom", dog);
stream.writeObject(p1);
Person p2 = new Person("bob", dog);//同一个对象只会被序列化一次 这里的dog只是一个引用编号
//每个序列化后的对象在磁盘中都有1个编号
stream.writeObject(p2);
//反序列化
FileInputStream inputStream = new FileInputStream("obj.txt");
ObjectInputStream objectInputStream = new ObjectInputStream(inputStream);
Person p1 = (Person) objectInputStream.readObject();
Person p2 = (Person) objectInputStream.readObject();
System.out.println(p1.pet==p2.pet);//true
objectInputStream.close();
//两个类都要实现序列化接口
class Person implements Serializable{
String name;
Pet pet;
public Person(String name, Pet pet) {
this.name = name;
this.pet = pet;
}
}
class Pet implements Serializable{
String name;
public Pet(String name) {
this.name = name;
}
}
//多次写入同一个对象时 第二次以及后面写入时如果修改了对象的属性值是无效的
Pet dog = new Pet("dog");
Person p1 = new Person("tom", dog);
stream.writeObject(p1);
dog.name="small dog";//这里虽然修改了name 但是写入的还是原来的"dog"
Person p2 = new Person("bob", dog);
stream.writeObject(p2);
对象类不仅可以写对象数据,基本类型的包装类型也可以写入
FileOutputStream outputStream = new FileOutputStream("obj.txt");
ObjectOutputStream stream = new ObjectOutputStream(outputStream);
stream.writeInt(123);
stream.writeByte(1);
stream.writeDouble(3.14);
stream.close();
FileInputStream inputStream = new FileInputStream("obj.txt");
ObjectInputStream objectInputStream = new ObjectInputStream(inputStream);
System.out.println(objectInputStream.readInt());//读出顺序和吸入顺序需要一致
System.out.println(objectInputStream.readByte());
System.out.println(objectInputStream.readDouble());
IntetAddress表示IP地址,有Intet4Address和Intet6Address两个子类
public static void main(String[] args) throws IOException {
/**
* 获取远程的InetAddress 对象
*/
InetAddress address = InetAddress.getByName("www.baidu.com");
System.out.println("是否可达: "+address.isReachable(2000));//true
System.out.println(address.getHostName());//www.baidu.com
System.out.println(address.getHostAddress());//14.215.177.39
/**
* 获取本地的netAddress 对象
*/
InetAddress localHost = InetAddress.getLocalHost();
System.out.println(localHost.getHostAddress());//10.170.11.18 本地机器的ip地址
System.out.println(localHost.getHostName());//DESKTOP-GQBJPB6 计算机名称
}
URLDecoder和URLEncoder
String keyWord="中国";
String encode = URLEncoder.encode(keyWord, "utf-8");
System.out.println(encode);//%E4%B8%AD%E5%9B%BD 可以看到是6个字节 因为u8编码每个汉字是3个字节
String decode = URLDecoder.decode(encode, "utf-8");
System.out.println(decode);//中国
TCP通信
//服务端代码
public static void main(String[] args) throws IOException {
ServerSocket serverSocket = new ServerSocket(9999);
List<Socket> sockets=new ArrayList<>();
while (true){
Socket accept = serverSocket.accept();
sockets.add(accept);//
//开启一个单独的线程处理连接上来的客户端
//不开启多线程的话 代码会一直在content=bufferedReader.readLine()阻塞 导致不能连接下一个客户端
new Thread(()->{
try {
InputStream inputStream = accept.getInputStream();
InputStreamReader inputStreamReader = new InputStreamReader(inputStream);
BufferedReader bufferedReader = new BufferedReader(inputStreamReader);
String content=null;
//服务端收发信息都是用到客户端socket的输入输出流
while((content=bufferedReader.readLine())!=null){
System.out.println("服务端收到: "+content);
for (Socket socket : sockets) {
if(socket==accept){
continue;//不发送信息给自己
}
OutputStream outputStream = socket.getOutputStream();
PrintStream printStream = new PrintStream(outputStream);
printStream.println(content);
printStream.close(); //关闭会导致客户端socket
}
}
} catch (IOException e) {
e.printStackTrace();
}
}).start();
}
}
//客户端代码
public static void main(String[] args) throws IOException {
Socket socket = new Socket("127.0.0.1", 9999);
//负责接受消息的线程
new Thread(()->{
try {
InputStream inputStream = socket.getInputStream();
InputStreamReader inputStreamReader = new InputStreamReader(inputStream);
BufferedReader bufferedReader = new BufferedReader(inputStreamReader);
String s=null;
while((s=bufferedReader.readLine())!=null){
System.out.println(s);
}
} catch (IOException e) {
e.printStackTrace();
}
}).start();
//主线程负责发送消息
OutputStream outputStream = socket.getOutputStream();
//包装成打印流
PrintStream printStream = new PrintStream(outputStream);
Scanner scanner = new Scanner(System.in);
String content=null;
while((content=scanner.nextLine())!=null){
printStream.println(content);
}
socket.close();
}
半关闭状态
socket.close()
, 但这样会导致一个问题,客户端只是不想发送数据了,关闭socket会导致也不能接受数据public static void main(String[] args) throws IOException {
ServerSocket serverSocket = new ServerSocket(9999);
Socket socket = serverSocket.accept();
InputStream inputStream = socket.getInputStream();
InputStreamReader inputStreamReader = new InputStreamReader(inputStream);
BufferedReader bufferedReader = new BufferedReader(inputStreamReader);
String s=null;
while((s=bufferedReader.readLine())!=null){
System.out.println(s);
}
System.out.println("读取结束...");
System.in.read();//防止服务器主线程结束
}
public static void main(String[] args) throws IOException {
Socket socket = new Socket("127.0.0.1", 9999);
OutputStream outputStream = socket.getOutputStream();
PrintStream printStream = new PrintStream(outputStream);
printStream.println("这是第一条消息");
printStream.println("这是第二条消息");
printStream.println("这是最后一条消息");
System.in.read();//防止客户端主线程结束
}
//虽然客户端数据发送完毕了 但是服务器还是一直在等待数据的发送
//在printStream.println("这是最后一条消息");后加上socket.shutdownOutput();则通知服务器客户度消息已经发送完毕了
//相当于四次挥手的前两次挥手操作
UDP通信
//服务端
public static void main(String[] args) throws IOException {
DatagramSocket socket = new DatagramSocket( 9999,InetAddress.getByName("localhost"));
DatagramPacket outPacket = new DatagramPacket(new byte[0], 0, InetAddress.getByName("localhost"), 9998);
byte[] inBuf = new byte[1024];
DatagramPacket inPacket = new DatagramPacket(inBuf,inBuf.length);
while (true){
socket.receive(inPacket);//接收客户端的消息
System.out.println("客户端ip: "+inPacket.getAddress());
System.out.println("客户度端口:"+inPacket.getPort());
String resp="我是服务端,我收到了消息: "+new String(inBuf,0,inPacket.getLength());
outPacket.setData(resp.getBytes());//发送消息给客户端
socket.send(outPacket);
}
}
//客户端
public static void main(String[] args) throws IOException {
DatagramSocket socket = new DatagramSocket( 9998, InetAddress.getByName("localhost"));
DatagramPacket outPacket = new DatagramPacket(new byte[0], 0, InetAddress.getByName("localhost"), 9999);
byte[] inBuf = new byte[1024];
DatagramPacket inPacket = new DatagramPacket(inBuf,inBuf.length);
Scanner scanner = new Scanner(System.in);
String s=null;
while((s=scanner.nextLine())!=null){
outPacket.setData(s.getBytes());
socket.send(outPacket);//发送给服务端
socket.receive(inPacket);//接受服务端的消息
System.out.println(new String(inBuf,0,inPacket.getLength()));
}
}
//接收包和发送包的端口都是目的端口
UPD实现广播通信
public static void main(String[] args) throws IOException {
MulticastSocket multicastSocket = new MulticastSocket(9999);
//广播地址:224.0.0.0~239.255.255.255
InetAddress address = InetAddress.getByName("230.0.0.1");
multicastSocket.joinGroup(address);
multicastSocket.setLoopbackMode(false);
DatagramPacket outPacket = new DatagramPacket(new byte[0], 0, address, 9999);
byte[] inBuf = new byte[1024];
DatagramPacket inPacket = new DatagramPacket(inBuf,inBuf.length);
new Thread(()->{
//接收数据
while (true) {
try {
multicastSocket.receive(inPacket);
System.out.println(new String(inBuf, 0, inPacket.getLength()));
} catch (IOException e) {
e.printStackTrace();
}
}
}).start();
Scanner scanner = new Scanner(System.in);
String s=null;
//发送数据
while((s=scanner.nextLine())!=null){
outPacket.setData(s.getBytes());
multicastSocket.send(outPacket);
}
}
MulticastSocket multicastSocket = new MulticastSocket(9999);
, 端口不冲突端口问题
java程序运行在JVM进程中,即使这个java程序中有多个线程(一个进程里面可以有多个线程,这个进程就是JVM进程),每运行一次JVM进程,上一次运行时JVM内存中的变量数据都会丢失,可以有多个JVM进程,多个进程之间相互独立,不会共享数据
类的加载分为三个阶段
class文件来源:本地的class文件 jar包中的class文件 从网络加载class文件 把一个Java源文件动态编译并完成加载
类的初始化时机:
static fianl String s="hello"
static final String s=System.currentMills()+""
同一个类只会被JVM加载到内存一次,怎么算同一个类? 由类的全限定类名和类加载器确定,即使全限定类名相同,类加载器不同,也是不同的类
类加载器分类
类加载机制:
public static void main(String[] args) {
ClassLoader systemClassLoader = ClassLoader.getSystemClassLoader();//系统类加载器
System.out.println(systemClassLoader);//sun.misc.Launcher$AppClassLoader@18b4aac2
ClassLoader classLoader = Demo1.class.getClassLoader();//当前类的加载器
System.out.println(classLoader);//sun.misc.Launcher$AppClassLoader@18b4aac2
System.out.println(systemClassLoader==classLoader);//true 当前类的加载器就是系统类加载器
//获取系统类加载器路径
//如果配置CLASSPATH环境变量会打印环境变量 否则打印当前类的路径
URL resource = systemClassLoader.getResource("");
System.out.println(resource);//file:/D:/IdeaSpace/java_basis/out/production/java_basis/
ClassLoader extendsLoader = systemClassLoader.getParent();
System.out.println(extendsLoader);//sun.misc.Launcher$ExtClassLoader@74a14482
ClassLoader rootLoader = extendsLoader.getParent();
System.out.println(rootLoader);//null 根加载器不是java实现的
}
类加载器加载Class步骤(不包含连接、初始化)
自定义类加载器
public class MyClassLoader extends ClassLoader{
// 包路径
private String Path;
// 构造方法,用于初始化Path属性
public MyClassLoader(String path) {
this.Path = path;
}
// 重写findClass方法,参数name表示要加载类的全类名(包名.类名)
@Override
protected Class<?> findClass(String name) throws ClassNotFoundException {
System.out.println("findclass方法执行");
// 检查该类的class文件是否已被加载,如果已加载则返回class文件(字节码文件)对象,如果没有加载返回null
Class<?> loadedClass = findLoadedClass(name);
// 如果已加载直接返回该类的class文件(字节码文件)对象
if (loadedClass != null){
return loadedClass;
}
// 字节数组,用于存储class文件的字节流
byte[] bytes = null;
try {
// 获取class文件的字节流
bytes = getBytes(name);
} catch (Exception e) {
e.printStackTrace();
}
if (bytes != null){
// 如果字节数组不为空,则将class文件加载到JVM中
System.out.println(bytes.length);
// 将class文件加载到JVM中,返回class文件对象
Class<?> aClass = this.defineClass(name, bytes, 0, bytes.length);
return aClass;
}else {
throw new ClassNotFoundException();
}
}
// 获取class文件的字节流
private byte[] getBytes(String name) throws Exception{
// 拼接class文件路径 replace(".",File.separator) 表示将全类名中的"."替换为当前系统的分隔符,File.separator返回当前系统的分隔符
String FileUrl = Path + name.replace(".", File.separator) + ".class";
byte[] bytes;
// 相当于一个缓存区,动态扩容,也就是随着写入字节的增加自动扩容
ByteArrayOutputStream arrayOutputStream = new ByteArrayOutputStream();
File file = new File(FileUrl);
// 创建输入流
InputStream inputStream = new FileInputStream(file);
int content;
// 循环将输入流中的所有数据写入到缓存区中
while ((content = inputStream.read()) != -1){
arrayOutputStream.write(content);
arrayOutputStream.flush();
}
bytes = arrayOutputStream.toByteArray();
return bytes;
}
}
public static void main(String[] args) throws ClassNotFoundException, IllegalAccessException, InstantiationException {
//构造器的参数是类的class文件所在的目录
MyClassLoader myClassLoader = new MyClassLoader("D:\\IdeaSpace\\java_basis\\out\\production\\java_basis\\");
Class<?> loadClass = myClassLoader.findClass("test.Hello");
Class<?> loadClass2 = myClassLoader.findClass("test.Hello");
System.out.println(loadClass==loadClass2);//true 相同的类加载器某个类只会被加载一次
MyClassLoader myClassLoader2 = new MyClassLoader("D:\\IdeaSpace\\java_basis\\out\\production\\java_basis\\");
Class<?> loadClass3 = myClassLoader2.findClass("test.Hello");
System.out.println(loadClass==loadClass3);//false 相同的类 但是是不同的类加载器(创建了两个MyClassLoader对象)
Object o = loadClass.newInstance();
System.out.println(o);//test.Hello@6d6f6e28
ClassLoader myLoader = loadClass.getClassLoader();
ClassLoader appLoader = myLoader.getParent();
ClassLoader extendsLoader = appLoader.getParent();
ClassLoader rootLoader = extendsLoader.getParent();
System.out.println(myLoader);//test.MyClassLoader@1540e19d
System.out.println(appLoader);//sun.misc.Launcher$AppClassLoader@18b4aac2
System.out.println(extendsLoader);//sun.misc.Launcher$ExtClassLoader@7f31245a
System.out.println(rootLoader);//null
}
获得Class对象
Class对象和普通对象一样,里面有很多方法
public static void main(String[] args) {
Person person = new Person();
Class<? extends Person> personClass = person.getClass();
/**
* 获取Person类中的构造器
* 输出结果
* public test.Person()
* public test.Person(java.lang.String,int)
*/
Constructor<?>[] constructors = personClass.getConstructors();
for (Constructor<?> constructor : constructors) {
System.out.println(constructor);
}
/**
* 获取Person类中的方法
* 会打印方法签名 不包括父类中的方法
* static int test.Person.f()
* void test.Person.info(java.lang.String)
*/
Method[] methods = personClass.getDeclaredMethods();
for (Method method : methods) {
System.out.println(method);
}
/**
* 获取Person类中的属性
* java.lang.String test.Person.name
* int test.Person.age
* static int test.Person.a
*/
Field[] fields = personClass.getDeclaredFields();
for (Field field : fields) {
System.out.println(field);
}
}
//带Declared的获得的方法或属性和访问权限无关
//不带Declared只能获得public权限的方法或属性
使用反射创建对象
Class<Person> personClass = Person.class;
Person person1 = personClass.newInstance();//调用无参构造器创建对象
Constructor<Person> constructor1 = personClass.getConstructor();//获取无参构造器
Person person2 = constructor1.newInstance();
Constructor<Person> constructor2 = personClass.getConstructor(String.class, int.class);//获取有参构造器
Person person3 = constructor2.newInstance("tom", 18);
System.out.println(person1);//Person{name='null', age=0}
System.out.println(person2);//Person{name='null', age=0}
System.out.println(person3);//Person{name='tom', age=18}
使用反射调用方法
Class<Person> personClass = Person.class;
Person person = new Person();
Method f1 = personClass.getMethod("f1", int.class);
Object ret1 = f1.invoke(person, 999);//f1是成员方法 第一个参数需要一个peron对象
System.out.println(ret1);//返回1
Method f2 = personClass.getMethod("f2", String.class, int.class);
Object ret2 = f2.invoke(null, "hello", 123);//f2是一个静态方法 不需要对象 所以第一个参数为null
System.out.println(ret2);//f2是void类型 返回null
public class Person {
public int f1(int a){
System.out.println("a="+a);
return 1;
}
public static void f2(String s,int a){
System.out.println("s="+s);
System.out.println("a="+a);
}
}
访问成员变量的值
Class<Person> personClass = Person.class;
Person person = new Person();
System.out.println(person.a);//1
Field a = personClass.getDeclaredField("a");
a.set(person,111);//通过反射修改a的值 a是成员变量 第一个参数需要传入一个person对象
System.out.println(person.a);//111
Field b = personClass.getDeclaredField("b");
b.setAccessible(true);//b是private 需要先取消权限访问检查
b.set(person,222);
Field c = personClass.getDeclaredField("c");
System.out.println(Person.c);//3
c.set(null,333);//c是静态变量 设置值时第一个参数为null即可
System.out.println(Person.c);//333
public class Person {
public int a=1;
private int b=2;
static int c=3;
}
使用反射操作数组
public static void main(String[] args) throws Exception {
Object arr = Array.newInstance(int.class, 10);//创建大小为10的int类型数组
Array.set(arr,1,100);//给数组元素赋值 arr[1]=100
Array.set(arr,2,200);
Object ele = Array.get(arr, 1);//获取数组元素的值
System.out.println(ele);
/**
* 使用Array获取数组属性
*/
int[][] nums=new int[3][4];
getArrayDimensions(nums);
}
public static void getArrayDimensions(Object arr){
if(arr.getClass().isArray()){
System.out.println(Array.getLength(arr));//打印数组维度
getArrayDimensions(Array.get(arr,0));
}
}
JDK动态代理步骤
//1. 创建一个接口和实现类(被代理的类)
interface Dog{
void info();
String say(String name,int age);
}
class JinMao implements Dog{
@Override
public void info() {
System.out.println("我是一只金毛");
}
@Override
public String say(String name, int age) {
return "金毛: name="+name+" age="+age;
}
}
class Keji implements Dog{
@Override
public void info() {
System.out.println("我是一只柯基");
}
@Override
public String say(String name, int age) {
return "柯基: name="+name+" age="+age;
}
}
//2. 创建一个InvocationHandler
class MyInvocationHandler implements InvocationHandler{
Dog dog;//被代理的对象 用接口类型表示 可以接受多个子类对象
public void setDog(Dog dog) {
this.dog = dog;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println(method.getName()+"方法开始执行...");
System.out.println(proxy.getClass());//class test.$Proxy0
if(args!=null){
System.out.println("参数:"+ Arrays.toString(args));
}
Object result = method.invoke(dog, args);//反射调用方法 传入一个对象
System.out.println(method.getName()+"方法执行结束...");
return result;
}
}
//3. 创建代理对象
// Dog dog = new JinMao();
Dog dog=new Keji();
MyInvocationHandler myInvocationHandler = new MyInvocationHandler();
myInvocationHandler.setDog(dog);
//只能强转为接口Dog类型 不能强转为子类类型
Dog dogProxy = (Dog) Proxy.newProxyInstance(dog.getClass().getClassLoader(), dog.getClass().getInterfaces(),myInvocationHandler);
//4. 代理对象调用方法
dogProxy.info();
dogProxy.say("tom",4);
反射与泛型
Class<Person> personClass = Person.class;
Field a = personClass.getDeclaredField("a");
System.out.println(a.getType());//int
Field intList = personClass.getDeclaredField("intList");
Field doubleList = personClass.getDeclaredField("doubleList");
Field map = personClass.getDeclaredField("map");
System.out.println(intList.getType());//interface java.util.List
System.out.println(intList.getGenericType());//java.util.List
System.out.println(doubleList.getType());//interface java.util.List
System.out.println(doubleList.getGenericType());//java.util.List
System.out.println(map.getType());//interface java.util.Map
System.out.println(map.getGenericType());//java.util.Map
public class Person {
int a=1;
List<Integer> intList;
List<Double> doubleList;
Map<String ,Integer> map;
}
//getType只能获取基本类型的形式 不能获取泛型参数信息 getGenericType可以获取泛型参数信息