赞
踩
本文适合Java入门和复习回顾,高级篇请参考导航:
导航:
【Java笔记+踩坑汇总】Java基础+JavaWeb+SSM+SpringBoot+SpringCloud+瑞吉外卖/黑马旅游/谷粒商城/学成在线+设计模式+面试题汇总+性能调优/架构设计+源码
本文最新版本:
目录
7.1 访问权限修饰符,public protected default private
15.8.1 字节缓冲输出流BufferedOutputStream
15.8.2 字节缓冲输入流BufferedInputStream
15.9.2 字符输出流OutputStreamWriter
15.9.7 字符缓冲流BufferedWriter和BufferedReader
15.11.1 标准输入输出流System.in和System.out
15.11.5 对象反序列化流ObjectInputStream
先下载jdk8或11,这个版本最稳定,下面是jdk8的下载链接:Java Downloads | Oracle
进入后选择windows64版本exe下载,安装后记得配置环境变量:
此电脑右键-属性-高级系统设置-环境变量:
- public class hello{
- public static void main(String[] args){
- System.out.println("fe");
- }
- }
Other Versions - IntelliJ IDEA
建议安装专业版, 右侧有数据库栏,写sql语句代码补全方便:
注意:所有设置都必须要关闭项目后,再进行设置,这样才是全局设置。
先关闭项目再点击设置
编码
maven,后面javaweb里用到
maven配置依赖和插件后自动刷新
查看本地历史记录:
idea是会自动保存各文件历史记录的,右键文件-查看本地历史记录即可:
先创建项目(file-new-project-empty project-起名-finish,SDK开发工具包选JDK11,语言级别也选11)、模块(项目右键-new-module-java-next-起名,SDK开发工具包选JDK11,语言级别也选11)、包(模块下src右键-new-package-起名-ok)、类(包右键new-java class)。
然后psvm回车自动生成静态main方法,sout回车自动生成System.out.println()输出方法。
在当前包内IO流File类创建文件,例如"1.txt",存的位置是在项目文件夹下,而不是包文件夹下。不确定可查看当前路径:
System.out.println(System.getProperty("user.dir"));
控制台中文乱码,就在设置里把文件编码页都改成utf-8。
导包:import 包名.类名;
格式化ctrl+alt+L,main方法psvm回车,输出sout回车,内容提示ctrl+alt+space。alt+insert生成构造、setget方法。
Ctrl+Alt+v或.var或.for补全代码:例如写new Dog();按快捷键会自动补全声明Dog dog=new Dog();或者写new Dog().var回车会自动补全成Dog dog=new Dog();或者写lists.for回车会自动生成for(List<String> list:lists){}
alt+enter:万能快捷键。获取警告、报错提示自动改正
ctrl+r:替换所有与选中内容相同的文本,出现
ctrl+b跟进:或者ctrl+鼠标左键、或者鼠标左键,跟进到该类的定义页面。如果选中的是标识符,可以查找页面内所有该标识符位置。
alt+左键:整列编辑
ctrl+d:复制光标行并粘贴
ctrl+x:删除光标行
Ctrl+h:查看该类继承关系:
ctrl+alt+m:抽取选中代码为方法
Ctrl+f12:查看类中所有方法。
普通断点:如果断点打在普通代码行上,点击“debug”会自动运行到该行暂停;
方法断点:如果断点打在方法上,点击“debug”会自动运行到该方法入口处暂停;
接口断点:如果断点打在接口方法,点击“debug”会自动运行到实现类对应方法入口处暂停;
字段断点:如果断点打在字段上,点击“debug”会自动运行到修改此字段的代码行处暂停(可以在断点处右键设置成访问时也暂停);
打断点后代码运行到这一步会停止:下面红色方框是最常用的两个调试按钮:
蓝色代码行代表此时调试运行到这一行(这一行还没运行),能看到上一行的结果。
调试的五个核心图标分别是:
- “Step over”:步过。往下运行一行代码并跳过当前方法的调用。
- “Step into”:步入。进入方法内部并继续逐行执行。适用于自定义方法或第三方方法,但无法进入JDK方法的内部。注意如果蓝色光标行不是方法,则此按钮相当于step over。
- “Force step into”:强制步入(不常用)。强制进入方法的内部,即使常规的"Step into"操作无法进入方法内部时也可以使用。适用于无法通过常规方式进入的方法。例如Symtem.out.println()等JDK方法内部。
- “Step out”:步出。退出当前方法,即执行完当前方法后返回到调用该方法的位置。注意如果本方法是main方法,则将直接步出这个main方法。
- “Resume Program”:运行到光标处。继续运行程序直到下一个断点位置或程序结束。可用于暂停状态下的程序恢复运行。
退帧:回退到此方法被调用之前。
当我们 Debug 从 A 方法进入 B 方法时,通过降帧(退帧)可以返回到调用 B 方法前,这样我们就可以再一次调用 B 方法。
通常用于当我们快执行完 B 方法后,发现某个重要流程被我们跳过了,想再看一下,则此时可以先回退到 A 方法,然后再次进入 B 方法。
右键并点击“force return” 后,此方法栈将直接终止,恢复到上个方法栈。
使用场景:需要结束当前断点,并且不希望接下来的程序操作数据库时,使用强制返回。
注意:注意如果点击红色方格或重新调试虽然也是终止,但它其实是运行完剩余代码后再终止。
- 强制返回:接下来的程序将不再继续执行。
- 终止:接下来的程序将走完,然后再终止程序。
在stream流处打断点,debug后点击“trace current stream chain”:
就可以看到整个流式处理的过程:
在if处打断点,debug后点击“evaluate expression”,可以在评估框下测试另一个分支:
多线程环境,打断点并右键设置Thread或All:
- import java.util.Random;
- public class hello{
- public static void main(String[] args){
- Random r=new Random();
- // 生成 0 到 10(不包括10)之间的随机整数
- int num=r.nextInt(10);
- System.out.println(num);
- }
- }
字符串不可变,创建后不可更改。
效果相当于字符数组,底层是字节数组。
- //构造
- String s1 = "Runoob"; // String 直接创建
- String s2 = new String("Runoob"); // String 对象创建
- String s3 = s1; // 相同引用
-
- int len = s1.length(); //长度
-
-
- //访问字符
- //访问要s1.charAt(0),不能s1[0];
-
- //格式化
- String s=String.format("%d",2);
- System.out.println(s);
-
-
- //拼接字符串、数字、字符
- s1="我的名字是 ".concat("Runoob");
- s2+="abc";
- s2+=123; //字符串+数字,数字会转成字符串
- s2+='c';
-
- //根据指定字符串分割
- s="1,2,3";
- String arr[]=s.split(",");
-
- //字符串翻转要自己写
-
- //数字转String
- String s=String.valueOf(12);
-
- //String转数字
- int c=Integer.parseInt(s);
-
- //String转Integer转数字
- int c=Integer.valueOf(s).intValue();
比较
- //比较地址
- //只要new,就在堆内存开辟空间。直接赋值字符串在常量池里。
- String str1 = "hello"; //常量池里无“hello”对象,创建“hello”对象,str1指向常量池“hello”对象。
- //先检查字符串常量池中有没有"hello",如果字符串常量池中没有,则创建一个,然后 str1 指向字符串常量池中的对象,如果有,则直接将 str1 指向"hello";
- String str2 = "hello"; //常量池里有“hello”对象,str2直接指向常量池“hello”对象。
- String str3 = new String("hello"); //堆中new创建了一个对象。假如“hello”在常量池中不存在,Jvm还会常量池中创建这个对象“hello”。
- String str4 = new String("hello");
- System.out.println(str1==str2);//true,因为str1和str2指向的是常量池中的同一个内存地址
- System.out.println(str1==str3);//fasle,str1常量池旧地址,str3是new出的新对象,指向一个全新的地址
- System.out.println(str4==str3);//fasle,因为它们引用不同
-
- //比较内容
- System.out.println(str4.equals(str3));//true,因为String类的equals方法重写过,比较的是字符串值
java中字符串:一个汉字长度是1,c++中字符串,一个汉字长度是2.
还有很多不同,不能弄混。
String拼接字符串后原字符串还存在于内存中,浪费内存。
StringBuffer 类的对象能够被多次的修改,并且不产生新的未使用对象,所以涉及到字符串拼接,优先用StringBuffer 。
- //构造
- StringBuilder sb= new StringBuilder("abc");
-
- //String转StringBuilder
- sb.append("d").append("e").append("f");
-
- //StringBuilder转String
- String s=sb.toString();
-
- //反转
- sb.reverse();
-
可以动态修改的数组,没有固定大小的限制。
- import java.util.ArrayList;
-
- public class RunoobTest {
- public static void main(String[] args) {
- ArrayList<String> sites = new ArrayList<String>();
- sites.add("Google");
- sites.add("Runoob");
- sites.add("Taobao");
- sites.add("Weibo");
- System.out.println(sites); //[Google, Runoob, Taobao, Weibo]
- System.out.println(sites.get(1)); // 访问第二个元素Runoob
- sites.set(2, "Wiki"); // 第一个参数为索引位置,第二个为要修改的值
- sites.remove(3); // 删除第四个元素
- System.out.println(sites.size());//计算大小
-
- }
- }
要点:直接退出程序System.exit(0);修改学生时没查成功提示失败,成功提示成功,方法是静态。查找id时查到break;id如果是int型,之后要用(char)System.in.read()接收空格或回车.
可优化:学号不存在和学号重复,加入地址、成绩、年龄等其他信息,查询制表。
- //StudentManager.java
- package package_test;
-
- import java.util.Scanner;
- import java.util.ArrayList;
-
- public class StudentManager {
- public static void main(String args[]) {
- Scanner sc=new Scanner(System.in);
- ArrayList<Student> al = new ArrayList<>();
- while (true) {
- System.out.println("1增加学生,2删除学生,3修改学生,4查询学生,5退出系统");
-
- int choose = sc.nextInt();
- switch (choose) {
- case 1:
- add(al);
- break;
- case 2:
- delete(al);
- break;
- case 3:
- update(al);
- break;
- case 4:
- search(al);
- break;
- default:
- System.exit(0);
- }
- }
- }
-
- public static void add(ArrayList<Student> al) { //必须是静态方法,否则报错
- System.out.println("input id:");
- Scanner sc = new Scanner(System.in);
- Student child = new Student();
- String id = sc.nextLine();
- child.setId(id);
- System.out.println("input name: ");
- String name = sc.nextLine();
- child.setName(name);
- al.add(child);
- }
-
- public static void search(ArrayList<Student> al) {
- if (al.size() == 0) System.out.println("no information");
- Scanner sc = new Scanner(System.in);
- for (int i = 0; i < al.size(); i++) System.out.println(al.get(i).getId() + " " + al.get(i).getName());
- }
-
- public static void update(ArrayList<Student> al) {
- System.out.println("input id");
- Scanner sc = new Scanner(System.in);
- Student child = new Student();
- child.setId(sc.nextLine());
- System.out.println("Please enter the modified name");
- child.setName(sc.nextLine());
- int find = 0;
- for (int i = 0; i < al.size(); i++) {
- if (al.get(i).getId().equals(child.getId())) {
- find = 1;
- al.set(i, child);
- break;
- }
- }
- if (find == 0) {
- System.out.println("not found");
- } else {
- System.out.println("successful");
- }
- search(al);
- }
-
- public static void delete(ArrayList<Student> al) {
- System.out.println("input id");
- Scanner sc = new Scanner(System.in);
- String ipt = sc.nextLine();
- for (int i = 0; i < al.size(); i++) {
- if (al.get(i).getId().equals(ipt)) {
- System.out.println("successful");
- al.remove(i);
- return;
-
- }
- }
- System.out.println("not exsit");
- search(al);
- }
- }
- //Student.java
- package package_test;
-
- public class Student {
- private String id;
- private String name;
-
- public Student(String id, String name) {
- this.name = name;
- this.id = id;
- }
-
- public Student() {
-
- }
-
- public String getId() {
- return id;
- }
-
- public void setId(String id) {
- this.id = id;
- }
-
- public String getName() {
- return name;
- }
-
- public void setName(String name) {
- this.name = name;
- }
- }
所有类都间接或直接继承自该类。
自动重写toString()和equlas():在类中ctrl+insert。不重写默认比较地址。
- public boolean equals(Object o) {
- if (this == o) return true; //地址相同直接true
- if (o == null || getClass() != o.getClass()) return false; //是否空或者不来自于同一个类
-
- Dog dog = (Dog) o;
-
- return weight == dog.weight;
- }
-
- @Override
- public int hashCode() {
- return weight;
- }
-
- @Override
- public String toString() {
- return "Dog{" +
- "weight=" + weight +
- '}';
- }
System类不能被实例化,要通过类名调用方法。
练习:获取for循环十万次的运行时长。
- public class Test {
- public static void main(String[] args) {
- long start=System.currentTimeMillis();
- for(int i=0;i<100000;i++){}
- long end=System.currentTimeMillis();
- System.out.println("for循环十万次的运行时长:"+(end-start)+"毫秒");
- }
- }
结果:
java.lang包下。
基本数据类型封装成对象好处是:可以有更多的方法操作改数据。
- //装箱:基本数据类型转为包装类
- Integer a=Integer.valueOf(32);
- Integer a2=32;
-
- //拆箱:包装类转为基本数据类型
- int b=a.intValue();
-
- //数字转String
- String s=String.valueOf(12);
-
- //String转数字
- int c=Integer.parseInt(s);
-
- //String转Integer转数字
- int c=Integer.valueOf(s).intValue();
getTime() 获取从1970年1月1日 0时0分0秒到此刻毫秒值
setTime() 自定义设置毫秒值
after()
判断某个日期是否在指定日期之后
before()
判断某个日期是否在指定日期之前
a.compareTo(b)
对两个日期进行比较
如果a时间在b之后,则返回1
如果a时间在b之前,则返回-1
如果a==b,则返回0
- Date date=new Date();
- date.setTime(234);
- System.out.println(date.getTime()); //输出234
为了格式化输出日期。
- public static void main(String[] args) throws ParseException { //throws只是把异常抛出,延迟处理。用trycatch处理比较好
- //Date转String
- Date d=new Date();
- SimpleDateFormat sdf=new SimpleDateFormat("yyyy年MM月dd日 HH:mm:ss");
- String str=sdf.format(d);
- System.out.println(str);
- //String转Date
- d=sdf.parse(str);
- System.out.println(d);
- }
- import java.util.Scanner;
- public class hello{
- public static void main(String[] args){
- Scanner sc=new Scanner(System.in);
- int i=sc.nextInt(); //nextLine()是带空格的一行字符串
- System.out.println(i);
- }
- }
-
- next() 不能得到带有空格的字符串,nextLine()可以。
- //注意:nextInt接下来nextLine,字符串会直接读取输入的数字之后的空格、回车,例如输入23回车,结果a的值是回车。
- //int b=sc.nextInt();String a=sc.nextLine();
java的八大基本数据类型分别是:
- //定义数组
- int []a;
- int b[];
- int[][] x,y;//x和y都是二维数组
- int[] z,t[];//z是一维数组,t是二维数组
-
- //初始化,栈内存存储局部变量,使用后立即消失。new申请堆内存空间,使用后会在垃圾回收期空闲时回收
- int d[]=new int[3];//动态初始化
- int c[]={1,2,3};//或int c[]=new int[]{1,2,3};静态初始化
-
- //直接输出数组会输出地址
- System.out.println(a); //[I@5b444398
- //数组转字符串
- System.out.println(Arrays.toString(c)); //[1, 2, 3]
-
- //多数组指向相同堆内存,改动e内容后,c内容也会改变
- int e[]=c;
重载(Overload):指一个类中可以有多个方法具有相同的方法名,但这些方法的参数类型不同、个数不同、顺序不同。
注意:方法返回值和访问修饰符可以不同。
示例:
- public class hello {
- public static void main(String args[]) {
- f();
-
- }
-
- public static void f() {
-
- System.out.println("3f");
- }
-
- public static void f(int a) { //重载,注意返回值同,参数不同
-
- System.out.println(a);
- }
- // 下面两个注释的方法就不是重载,会报错
- // public static int f(){ //返回值不同
- // System.out.println("hello");
- // }
- // void f(){ //修饰符不同
- // return 666;
- // }
- }
(int... a)是将所有int参数封装到a数组里。
注意可变参数要放在后面。例如(int a,int... b)正确,(int... a,int b)会报错。
- public class Phone {
-
- public Phone() {
- System.out.println("hhh");//创建对象时会直接运行构造方法
- }
- }
public : 对所有类可见。使用对象:类、接口、变量、方法
protected : 对同包可见、对不同包子类可见。使用对象:变量、方法。 注意:不能修饰类(外部类)。
default : 同包可见。使用对象:类、接口、变量、方法。
private : 在同一类内可见。使用对象:变量、方法。 注意:不能修饰类(外部类)
修饰符 | 当前类 | 同一包内 | 子孙类(同一包) | 子孙类(不同包) | 其他包 |
---|---|---|---|---|---|
public | Y | Y | Y | Y | Y |
protected | Y | Y | Y | Y/N(说明) | N |
default | Y | Y | Y | N | N |
private | Y | N | N | N | N |
在同一类内可见,保护成员不被别的类使用。可以修饰变量、方法。 注意:不能修饰类(外部类)
- public class Phone {
- int price;//成员变量在堆内存
- private int age;
-
- public void setAge(int a) {
- age = a;
- }
- //或者用this
- // public void setAge(int age) {
- // this.age = age;
- // }
-
- public int getAge() {
- return age;
- }
- }
-
-
- //使用
- public class hello {
- public static void main(String args[]) {
- Phone p = new Phone();
- p.setAge(12);
- System.out.println(p.getAge());
-
- }
-
- }
静态成员变量被所有对象共享,可用类名调用。局部变量不能被声明为 static 变量。
- public class Phone {
- static int price;//成员变量在堆内存
- }
-
-
- //使用
- public class hello {
- public static void main(String args[]) {
- Phone.price=4;
- //fun();会报错,静态方法只能访问静态方法或变量。
- }
- public fun(){};
- }
静态方法只能访问静态变量和方法。非静态方法都可以访问。
静态方法中不能使用 this 关键字,因为在静态方法的局部变量表中并不存在this变量。
- public abstract class Animal{
- abstract void m(); //抽象方法
- abstract void n(); //抽象方法
- }
-
- class Dog extends Animal{ //非抽象子类
- //实现父类所有抽象方法
- void m(){
- .........
- }
- void n(){
- .........
- }
- }
-
-
- abstract class Cat extends Animal{}//抽象子类,不需要重写父抽象类的抽象方法
①指代当前类中的成员变量、方法。可以用于方法的形参与成员变量同名时进行区分,
- public class Phone {
- private int age;
-
- public void setAge(int age) {
- this.age = age;
- }
-
- public int getAge() {
- return age;
- }
- }
指代父类的成员变量、方法。
看懂下面代码基本就能区分this和super
- public class Animal {
- public String name;
- int age=3;
- public void eat() { //吃东西方法的具体实现 }
- public void sleep() { //睡觉方法的具体实现 }
- }
- //子类
- public class Penguin extends Animal{
- int age=4;
- public void show(){
- int age=5;
- System.out.println(age); //5,子类可以访问父类非私有成员变量
- System.out.println(this.age);//4
- System.out.println(super.age);//3
- }
- }
- //定义类
- public class Phone {
- int price;//对象在堆内存,成员变量便也在堆内存,创建对象时有初始值。
-
- public void fun() {
- int tmp;//局部变量在栈内存,不赋值就用会报错
- System.out.println("phone");
- }
- }
-
- //创建对象,建议先写new Phone()然后ctrl+alt+v补全声明
- Phone p=new Phone();
局部变量必须赋初值,不然会报错。
内部类:在一个类中定义类。分为成员内部类(成员位置),局部内部类(成员方法内),匿名内部类(方法内)。
匿名内部类:一个继承了其他类或者实现了其他接口的子类对象。
内部类可以直接访问外部类私有、公有成员。外部类要访问内部类成员要创建对象。
- public class Outer {
- int num=10;
- //定义成员内部类:在成员位置定义类
- public class Inner{ //一般private,用外部类访问内部类更安全
- public void show(){
- System.out.println("innershow"+num); //内部类可直接访问外部类成员。
- }
- }
-
- //外部类访问成员内部类
- public void fun(){
- Inner in=new Inner();
- in.show();
- }
-
- //局部内部类:在成员方法内定义类
- public void fun2(){
- class Inner2{ //成员内部类不能public和private
- void show(){
- System.out.println("成员内部类");
- }
- }
- Inner2 in=new Inner2();
- in.show();
- }
-
- //匿名内部类:在成员方法内定义子类,实现接口或继承抽象类
- public void fun3(){
- Phone p=new Phone() { //自己写的Phone接口
- @Override
- public void show() {
- System.out.println("实现接口的匿名内部类");
- }
- }; //注意是一个语句有分号
- p.show();
- }
- }
-
-
- //内部类创建对象(一般内部类私有,使用外部类访问内部类)
- public class Test {
- public static void main(String[] args) {
- Outer.Inner i=new Outer().new Inner();
- i.show();
- }
- }
将类的某些信息隐藏在类的内部,不允许外部程序直接访问。
- public class Phone {
- int price;//成员变量在堆内存
- private int age;
-
- public void setAge(int age) {
- this.age = age;
- }
-
- public int getAge() {
- return age;
- }
- }
好处:安全性和复用性。
优点:提高代码复用性,维护性。缺点:高耦合性,父类改变子类也会改变。
特点:子类拥有父类非 private 的属性、方法。可以拥有自己的属性和方法,即子类可以对父类进行扩展。可以用自己的方式实现父类的方法。
注意: Java 不支持多继承,但支持多重继承。
单继承是子类只能继承一个父类。相对于实现是一个子类可以
子类所有构造方法会默认先运行super();
- public class Animal {
- public String name;
- int age=3;
- public Animal(String name, int age) {
- //初始化属性值
- }
- public void show() { //吃东西方法的具体实现 }
- public void sleep() { //睡觉方法的具体实现 }
- }
- //子类
- public class Penguin extends Animal{
- int age=4;
- public Penguin(){
- super(); //不写也会默认运行
- }
- @Override //注解重写,检查重写是否正确。例如修饰符(子类重写方法的访问权限要≥父类)、函数名错误。
- public void show(){ //重写父类中show(),如果去掉public会报错。想再用super.show();
- int age=5;
- System.out.println(age); //5,子类可以访问父类非私有成员变量
- System.out.println(this.age);//4
- System.out.println(super.age);//3
- }
- }
使用 implements 关键字可以变相的使java具有多继承的特性
- public interface A {
- public void eat();
- public void sleep();
- }
-
- public interface B {
- public void show();
- }
-
- public class C implements A,B {
- }
接口引用指向实现类对象
多态最常用的是接口引用指向实现类对象,这样当之后程序需要更新时候,只需要修改new后面的实现类即可,左边就不用修改了,从而降低耦合。
在Spring框架中,甚至可以通过配置或注解连实例化的new也不用了,从而更高层次的降低耦合。
示例:
List<String> a=new ArrayList<String>();
这样写的话,等号右边换成Vector 或 LinkedList 时,就可以很少修改代码量,降低耦合。
向上转型:
向上转型:父类引用指向子类对象。编译看左边(调用子类特有变量、方法会报错),运行看右边(优先运行子类重写后的方法)。
Animal a=new Cat();
向下转型:
向下转型:子类引用指向父类对象。编译看右边,运行看右边。
- public static void main(String[] args) {
-
- Animal a = new Cat(); // 向上转型
- a.eat(); // 调用的是 Cat 的 eat
- Cat c = (Cat)a; // 向下转型
- c.work(); // 调用的是 Cat 的 work
- }
抽象类是对事物的抽象,如动物类,小狗类,接口是对行为的抽象,如吃饭类、睡觉类。
除非实现接口的类是抽象类,否则该类要定义接口中的所有方法。
- public interface Phone {
- public static final int price=4; //修饰符可省略
- public abstract void show(); //修饰符可省略
- }
-
-
- public class PhoneImpl implements Phone{
- public void show(){ //必须是public,否则报错
- System.out.println("hello");
- }
- }
Throwable是所有异常的父类。
导图:
异常继承体系 :
RuntimeException是运行时异常,例如ArrayIndexOutOfBoundsException,编译不出错,运行出错,要try-catch处理。
非RuntimeException是编译时异常,throws可以抛出异常,使编译通过,运行时还会出错,要try-catch处理。
下面的列表是 Throwable 类的主要方法:
序号 | 方法及说明 |
---|---|
1 | public String getMessage() 返回关于发生的异常的详细信息。这个消息在Throwable 类的构造函数中初始化了。 |
2 | public String toString() 返回此 Throwable 的简短描述。 |
3 | public void printStackTrace() 将此 Throwable 及其回溯打印到标准错误流。。 |
程序运行出错,JVM会将异常的名称、原因、位置输出到控制台,并让程序停止运行。
throws只是抛出异常,并没有对异常进行实际处理,要用try-catch处理异常。
使用 try 和 catch 关键字可以捕获异常。try/catch 代码块放在异常可能发生的地方。
try/catch代码块中的代码称为保护代码,使用 try/catch 的语法如下:
- try
- {
- // 程序代码
- }catch(ExceptionName e1)
- {
- //Catch 块
- }
- int[] arr = {1, 2, 3};
-
- try {
- System.out.println(arr[10]);
- } catch (Exception e) { //ArrayIndexOutOfBoundsException
- e.printStackTrace();
- }
- System.out.println("有异常处理后程序不会中断,此处也能输出");
- public class Student {
- public void checkScore(int score) throws ScoreException{ //要加throws,声明此方法会抛出异常
- if(score>100||score<0)throw new ScoreException("wrong Score");
- else System.out.println("分属正常");
- }
- }
-
-
- public class Demo {
- public static void main(String[] args) {
- Student s=new Student();
- try{
- s.checkScore(111);
- }catch (ScoreException e){
- e.printStackTrace();
- }
- System.out.println("这里也能输出");
- }
- }
-
- public class ScoreException extends Exception { //自定义异常类
-
- public ScoreException() {
- }
-
- public ScoreException(String wrong_score) {
- super(wrong_score); //带参构造,异常时控制台输出指定字符串
- }
- }
ArrayList底层是数组,查询快(get,contain)增删(add,remove)慢,LinkedList底层是链表,查询慢,增删快。
元素不可重复的集合:HashXxx用hashCode()判断元素是否重复,TreeXxx用比较器。
Collection是add()和remove(),Map是put(),get().
HashXxx用hashCode()判断元素是否重复.
*HashMap* | *HashSet* |
HashMap实现了Map接口 | HashSet实现了Set接口 |
HashMap储存键值对 | HashSet仅仅存储对象 |
使用put()方法将元素放入map中 | 使用add()方法将元素放入set中 |
HashMap中使用键对象来计算hashcode值 | HashSet使用成员对象来计算hashcode值。 |
HashMap比较快,因为是使用唯一的键来获取对象 | HashSet较HashMap来说比较慢,contains()时间复杂度也不是O(1) |
相同点:单例集合,数据不可重复
不同点1:底层使用的储存数据结构不同:
1,Hashset底层使用的是HashMap哈希表结构储存
2,而Treeset底层用的是TreeMap树结构储存。
不同点2:储存的数据保存唯一方式不用。
1,Hashset是通过复写hashCode()方法和equals()方法来保证的。
2,而Treeset是通过Compareable接口的compareto方法来保证的。
不同点3:
hashset无序 Treeset有序
可以动态修改的数组,没有固定大小的限制。
- import java.util.ArrayList;
-
- public class RunoobTest {
- public static void main(String[] args) {
- List<String> sites = new ArrayList<String>();
- sites.add("Google"); //增
- sites.add("Runoob");
- sites.add("Taobao");
- sites.add("Weibo");
- sites.add("Ai");
- System.out.println(sites); //[Google, Runoob, Taobao, Weibo]
-
- Collections.sort(sites); //字母顺序排序
- Collections.sort(sites, new Comparator<String>() { //比较器长度排序
- @Override
- public int compare(String o1, String o2) {
- return o1.length()-o2.length();
- }
- });
-
- System.out.println(sites.get(1)); // 查,访问第二个元素Runoob
- System.out.println(sites.contains("Google")); //查,是否包含
- sites.set(2, "Wiki"); // 改,第一个参数为索引位置,第二个为要修改的值
- sites.remove(3); // 删除第四个元素
- System.out.println(sites.size());//计算大小
-
- //迭代器
- Iterator<String> it=sites.iterator();
- while(it.hasNext()) System.out.println(it.next());
- }
- }
List是接口,ArrayList是它的实现类
以下两种方法都可以,但是不提倡第二种:
- List list=new ArrayList();
- ArrayList list=new ArrayList();
List list=new ArrayList();好处
在设计模式中有对依赖倒置原则。程序要尽量依赖于抽象,不依赖于具体。 从Java语法上,这种方式是使用接口引用指向具体实现。
比如,你若希望用LinkedList的实现来替代ArrayList的话,只需改动一行即可,其他的所有的都不需要改动:
List list=new LinkedList();
这也是一种很好的设计模式.一个接口有多种实现,当你想换一种实现方式时,你需要做的改动很小.
面向接口编程
提高程序宽展性,以后修改维护好些
声明一个接口的变量(接口的引用)可以指向一个实现类(实现该接口的类)的实例, 但是该接口的变量不能使用实现类中有,接口中没有的方法(实现类中没有重写的方法,自添加的方法)。。
并发修改异常:
列表迭代器允许沿任一方向遍历列表
Modifier and Type | Method and Description |
---|---|
void | add(E e) 将指定的元素插入列表(可选操作)。 |
boolean | hasNext() 返回 |
boolean | hasPrevious() 返回 |
E | next() 返回列表中的下一个元素,并且前进光标位置。 |
int | nextIndex() 返回随后调用 |
E | previous() 返回列表中的上一个元素,并向后移动光标位置。 |
int | previousIndex() 返回由后续调用 |
void | remove() 从列表中删除由 |
void | set(E e) 用 指定的元素替换由 |
- ListIterator<String> it=sites.listIterator();
- while(it.hasNext()) System.out.println(it.next());
这样就不会出现并发修改异常。
内部原理是一个iterator迭代器。
方法 | 描述 |
---|---|
public void add(int index, E element) | 向指定位置插入元素。 |
public void addFirst(E e) | 元素添加到头部。 |
public void addLast(E e) | 元素添加到尾部。 |
public void clear() | 清空链表。 |
public E remove(int index) | 删除指定位置的元素。 |
public E removeFirst() | 删除并返回第一个元素。 |
public E removeLast() | 删除并返回最后一个元素。 |
public boolean contains(Object o) | 判断是否含有某一元素。 |
public E getFirst() | 返回第一个元素。 |
public E getLast() | 返回最后一个元素。 |
- public static void main(String[] args) {
- LinkedList<String> link=new LinkedList<String>();
- link.addLast("hello");
- link.addLast("world");
- for(String s:link) System.out.println(s);
- System.out.println(link);
- }
不包含重复元素,无序。
允许有null值,不是线程安全的。
无重原理:通过hashCode值来判断重复元素的。
int hashCode():返回哈希值。同一对象哈希值相同,不同对象的哈希值默认不同,方法重写后可以相同。
哈希表是元素为链表的数组,默认容量16,负载因子0.75,处理冲突方法是链地址法。
- import java.util.HashSet;
- import java.util.LinkedList;
-
- public class Test2 {
- public static void main(String[] args) {
- HashSet<String > h=new HashSet<String>();
- h.add("nihao");
- String s1=new String("nihao");
- h.add(s1);
- System.out.println(s1=="nihao"); //false
- for(String s:h) System.out.println(s); //不含重复元素
- }
- }
如果Hashset里的元素是对象,若想将成员变量相同视为对象相同,要重写hashCode():
- import java.util.HashSet;
-
- public class Test {
- public static void main(String[] args) { //输出23
- Dog dog1=new Dog(23);
- Dog dog2=new Dog(23);
- HashSet<Dog> h=new HashSet<Dog>();
- h.add(dog1);h.add(dog2);
- for(Dog dog:h) System.out.println(dog.weight);
- }
- }
- package package1;
-
- public class Dog extends Animal{
- int weight=4;
- public Dog(){
- // System.out.println("doggouzaao");
- }
- public Dog(int weight){
- this.weight=weight;
- }
- @Override
- public void show() {
- name="dogname";
- System.out.println("dog");
- }
-
- @Override
- public boolean equals(Object o) { //alt+insert生成equals()和hashCode()方法。这里其实只需要重写hashCode方法就能保证自动去重,equals方法用于元素间的比较
- if (this == o) return true;
- if (o == null || getClass() != o.getClass()) return false;
-
- Dog dog = (Dog) o;
-
- return weight == dog.weight;
- }
-
- @Override
- public int hashCode() {
- return weight;
- }
-
- @Override
- public String toString() {
- return "Dog{" +
- "weight=" + weight +
- '}';
- }
- }
存取有序,不重复。
“LinkedHashSet的底层数据结构是由hash表和双向链表实现,双向链表维护了访问的次序。”
有序,去重复。
TreeSet():自然顺序排序
TreeSet(Comperable c):比较器排序。
如果TreeSet内元素是基本数据类型,它会自动去重有序。
如果TreeSet内元素是对象,要实现去重有序,有两种方法。方法一,自然排序,类要实现Comparable<>接口,并重写compareTo(Student s)方法;方法二,比较器排序,带比较器构造TreeSet,否则报错。
方法一:自然排序
实现Comparable<>重写compareTo()方法
- import java.util.TreeSet;
-
- public class Test {
- public static void main(String[] args) {
- Dog dog1=new Dog(23);
- Dog dog2=new Dog(45);
- TreeSet<Dog> h=new TreeSet<Dog>();
- h.add(dog1);h.add(dog2);
- for(Dog dog:h) System.out.println(dog.weight);
- }
- }
- package package1;
-
- public class Dog extends Animal implements Comparable<Dog>{ //要实现Comperable<>并重写compareTo()方法
- int weight=4;
- public Dog(){
- }
- public Dog(int weight){
- this.weight=weight;
- }
- @Override
- public int compareTo(Dog dog){ //实参是上一只狗,本狗与上狗做比较
- return 1; //按存取顺序排序,本狗比上只狗大
- //return -1;存储逆序排序
- //return 0;视为相等,重复删除。
- //return this.weight-dog.weight;按年龄从小到大排序,后狗-前狗。
- }
- }
方法二:比较器排序
无需重写,带参构造,参数为比较器Comparator<>。
- TreeSet<Dog> h=new TreeSet<Dog>(new Comparator<Dog>() {
- public int compare(Dog a,Dog b){
- if(a.weight!=b.weight) return a.weight-b.weight;
- else return a.name.compareTo(b.name);
- }
- });
泛型本质是将具体的类型参数化,提供了编译时类型安全检测机制。
定义格式:<类型>、<类型1,类型2...>
泛型是编译时类型安全检测,示例:
- import java.util.ArrayList;
-
- public class Test2 {
- public static void main(String[] args) {
- Collection a=new ArrayList();
- Collection<String> b=new ArrayList<String>(); //泛型
- a.add("he");
- a.add(3);
- b.add("he");
- // b.add(3); 直接写时这里会报错,因为通过泛型指定了类型为String
- for(Object o:a) System.out.println(o);
- for(Object o:b) System.out.println(o);
- }
- }
泛型类将类型参数化、模板化,创建对象时再指定具体类型。
示例:
- package package2;
-
- public class Generic<T> {
- private T t;
-
- public T getT() {
- return t;
- }
-
- public void setT(T t) {
- this.t = t;
- }
- }
- package package2;
-
- //创建对象时再指定具体类型
- public class Test2 {
- public static void main(String[] args) {
- Generic<Integer> g=new Generic<Integer>(); //创建对象时指定具体类型为Integer
- g.setT(32);
- System.out.println(g.getT());
-
- Generic<String> g2=new Generic<String>(); //创建对象时指定具体类型为String
- g2.setT("hello");
-
- // g2.setT(34);指定了String,运行时安全检测报错
- System.out.println(g2.getT());
- }
- }
示例:
- package package2;
- public class Generic {
- public <T>void show(T t){
- System.out.println(t);
- }
- }
类型通配符:<?>
例如List<?>:表示元素类型未知的List,它的元素可以匹配任何类型。这种带通配符的List仅表示它是各种类型的父类,并不能把元素添加到其中
List<?> l0=new ArrayList<Number>();
类型通配符的上限:List<? extends 指定类型>
例:List<? extends Number> :它表示的类型是Number的子类型
List<? extends Object> l1=new ArrayList<Number>(); //通配object的子类
类型通配符的下限:List<? super 指定类型>
例:List<? super Number>:它表示的类型是Number的父类
List<? super Integer> l2=new ArrayList<Number>(); //通配Integer的父类
(int... a)是将所有int参数封装到a数组里。
注意可变参数要放在后面。例如(int a,int... b)正确,(int... a,int b)会报错。
Interface Map<K,V> k是键,V是值。
void | clear() 从这张地图中删除所有的映射。 |
boolean | isEmpty() 如果此地图不包含键值映射,则返回 true 。 |
V | put(K key, V value) 将指定的值与此映射中的指定键相关联。 |
V | get(Object key) 获取指定 key 对应对 value |
V | getOrDefault(Object key) 获取指定 key 对应对 value,如果找不到 key ,则返回设置的默认值 |
V | remove(Object key) 从该地图中删除指定键的映射(如果存在)。 |
boolean | containsKey(Object key) 如果此映射包含指定键的映射,则返回 true 。 |
boolean | containsValue(Object value) 如果此地图将一个或多个键映射到指定值,则返回 true 。 |
Set<K> | keySet() 返回此地图中包含的键的 |
Collection<V> | values() 返回此地图中包含的值的 |
Set<Map.Entry<K,V>> | entrySet() 返回此地图中包含的映射的 |
遍历方法1:keySet()获取键的Set集合,遍历Set集合,通过get()获取值。
方法2:entrySet()获取键值对Map.Entry<K,V>的Set集合,遍历Set集合,通过getKey()和getValue()获取键值。
- import java.util.*;
-
- public class Test2 {
- public static void main(String[] args) {
- //声明
- Map<String, String> map = new HashMap<>();
- //添加
- map.put("aaa","bbb");map.put("cc","dd");map.put("e","f");
- //获取,如果"aaa"对应value不为null则返回value;如果为null,则返回null。
- System.out.println(map.get("aaa"));
- //获取,如果"aaa"对应value不为null则返回value;如果为null,则返回"default"字符串。
- System.out.println(map.getOrDefault("aaa", "default"));
- //获取所有keys
- Set<String> strings = map.keySet();
- //获取所有values
- Collection<String> values = map.values();
- //遍历map
- Set<Map.Entry<String,String>> set=map.entrySet();
- for(Map.Entry<String,String> i:set){
- System.out.println(i.getKey()+i.getValue());
- }
- }
-
- }
如果HashMap键是对象,并且要求对象内成员变量值相等视为同一对象,则重写hashCode().
Modifier and Type | Method and Description |
---|---|
static <T extends Comparable<? super T>> | sort(List<T> list) 根据其元素的natural ordering对指定的列表进行排序。 |
static void | reverse(List<?> list) 反转指定列表中元素的顺序。 |
static void | shuffle(List<?> list) 使用默认的随机源随机排列指定的列表。 |
路径斜杠可以是//,/,\\,不能是\,因为它是转义符。
按数据流向:
输入流:读数据。
输出流:写数据。
按数据类型:
字节流:它处理单元为1个字节(byte),操作字节和字节数组,存储的是二进制文件。
应用范围:如果是音频文件、图片、歌曲,就用字节流好点(1byte = 8位)。
字符流:它处理的单元为2个字节的Unicode字符,分别操作字符、字符数组或字符串,字符流是由Java虚拟机将字节转化为2个字节的Unicode字符为单位的字符而成的。
应用范围:如果是关系到中文(文本)的,用字符流好点(1Unicode = 2字节 = 16位);
File封装的不是真正的文件,只是路径。
创建文件示例:
- import java.io.File;
- import java.io.IOException;
-
- public class Demo {
- public static void main(String[] args) throws IOException {
- File f=new File("D:\\1\\2.txt");
- //只创建已存在文件夹“D://1”下的2.txt。若“D://1”不存在,报错IOException: 系统找不到指定的路径。
- System.out.println(f.createNewFile()); //true,当目标位置已存在同名文件则创建失败输出false
- }
- }
注意:在当前包内IO流,使用File类创建文件
new File("1.txt");
默认存的位置是在项目文件夹下,而不是包文件夹下。
关于路径斜杠和反斜杠:
正斜杠“/”和“//”都可以,反斜杠必须“\\”,因为“\”会ASCII转义,“\\”在字符串里才是“\”。
File file = new File("D://1//1.txt"); File file1 = new File("D:/1/2.txt"); File file2 = new File("D:\\1\\3.txt"); // File file3 = new File("D:\1\4.txt"); //这样会报错,单个反斜杠会进行转义 System.out.println(file.createNewFile()); //true System.out.println(file1.createNewFile()); //true System.out.println(file2.createNewFile()); //true // System.out.println(file3.createNewFile()); //false查看当前路径:
System.out.println(System.getProperty("user.dir")); //D:\workspace\java\test
创建删除文件和文件夹的api:
boolean | createNewFile() 当且仅当具有该名称的文件尚不存在时,原子地创建一个由该抽象路径名命名的新的空文件。 |
boolean | mkdir() 创建由此抽象路径名命名的目录。 |
boolean | mkdirs() 创建由此抽象路径名命名的目录,包括任何必需但不存在的父目录。 |
boolean | delete() 删除由此抽象路径名表示的文件或目录。 |
File[] | listFiles() 返回一个抽象路径名数组,表示由该抽象路径名表示的目录中的文件。 |
举例:
- File f=new File("1\\2\\3");
- System.out.println(f.mkdirs());
- File f1=new File("1\\2\\3\\4.txt");
- System.out.println(f1.createNewFile());
-
- File[] f2=f.listFiles(); //listFiles()是路径下的所有文件组成的数组,若f文件会报错
- for(File file:f2){
- if(file.isFile()) System.out.println(file.getName()+","+file.getPath());
- }
- //删除
- //删除目录时要确保目录下没文件。
-
- f1.delete();f.delete();
返回false是因为文件夹和文件已存在。
概念:
Java中的 InputStream 和 OutputStream 都是 io 包中面向字节操作的顶级抽象类,关于java同步 io字节流的操作都是基于这两个的。
子类:
OutputStream类是所有输出字节流的超类:
常用api:
示例,OutputStream封装到BufferedWriter:
字节输出流OutputStream转字符输出流OutputStreamWriter转缓冲字符输出流BufferedWriter。s是Socket对象。
概念:
Java中的 InputStream 和 OutputStream 都是 io 包中面向字节操作的顶级抽象类,关于java同步 io字节流的操作都是基于这两个的。
子类:
InputStream是所有输入字节流类的超类:
常用api:
字节输入流InputStream封装到字符缓冲输入流BufferedWriter:
这里s是Socket对象:
是OutputStream子类。
输出流是写数据,把数据输出到文件里。
FileOutputStream(File file) 创建文件输出流以写入由指定的 |
FileOutputStream(String name) 创建文件输出流以指定的名称写入文件 |
FileOutputStream(String name, boolean append) 创建文件输出流以指定的名称写入文件。 |
默认彻底覆盖写入,append为true是追加写入。
示例,将“abcde” 写入1.txt文件中:
- FileOutputStream fos=new FileOutputStream("1.txt");
- byte b[]="abcde".getBytes();
- fos.write(b);
- fos.close();
完整版:
- import java.io.File;
- import java.io.FileOutputStream;
- import java.io.IOException;
-
- public class Demo {
- public static void main(String[] args) {
- FileOutputStream fos=null;
- try {
- fos=new FileOutputStream("z://1.txt");
- byte b[]="efe".getBytes();
- fos.write(b);
- fos.close();
- }catch (IOException e){
- e.printStackTrace();
- }finally {
- System.out.println("finally");
- if(fos!=null){ //防止空指针异常
- try {
- fos.close();
- } catch (IOException e) {
- e.printStackTrace();
- }
- }
- }
- }
- }
注意写数据时换行符,windows系统下是\r\n,linux的换行是\n,mac换行\r.
是InputStream子类。
FileInputStream(String name) 通过打开与实际文件的连接来创建一个 |
返回的int是读取字节的个数。
- FileInputStream fis=new FileInputStream("1.txt");
- byte b[]=new byte[1024]; //一般是1024整数倍
- int len=-1;
- while ((len= fis.read())!=-1) System.out.println(new String(b,0,len));
- fis.close();
常规方法:
- FileInputStream fis=new FileInputStream("D://1.png");
- FileOutputStream fos=new FileOutputStream("D://1//1.png");
- byte []b=new byte[1024];
- int len=fis.read(b);
- while(len!=-1){
- System.out.println(len);
- fos.write(b,0,len);len=fis.read(b);
- }
扩展,Maven简单方法:
学maven后再用这个方法。
使用IOUtils.copy(fis,os)进行流的复制(推荐)
(1)pom.xml添加依赖
<dependency> <groupId>commons-io</groupId> <artifactId>commons-io</artifactId> <version>2.6</version> </dependency>(2)调用工具类方法实现复制
//fis:输入流 //os:输出流 IOUtils.copy(fis,os);
特点:
字节缓冲流仅提供缓冲区,真正读写数据还要靠基本字节流对象操作。
字节缓冲流实现复制文件比基本字节流快很多。
该类实现缓冲输出流。 通过设置这样的输出流,应用程序可以向底层输出流写入字节,而不必为写入的每个字节导致底层系统的调用。
Constructor and Description |
---|
BufferedOutputStream(OutputStream out) 创建一个新的缓冲输出流,以将数据写入指定的底层输出流。 |
BufferedOutputStream(OutputStream out, int size) 创建一个新的缓冲输出流,以便以指定的缓冲区大小将数据写入指定的底层输出流。 |
当创建BufferedInputStream
时,将创建一个内部缓冲区数组。 当从流中读取或跳过字节时,内部缓冲区将根据需要从所包含的输入流中重新填充,一次有多个字节。
为什么要用字符流?
GBK编码,一个汉字占用2个字节。
utf-8编码,一个汉字占用3个字节。
汉字存储时,无论使用哪种编码,第一个字节都是负数。
所以字节流在读中文的时候有可能会读到半个中文,造成乱码
解决办法:
1.使用字符流(底层是字节流,字符流=字节流+编码表)
2.字节流直接操作字符,写出中文必须将字符串换成字节数组
- package com.heima.stream;
-
- import java.io.FileInputStream;
- import java.io.FileNotFoundException;
- import java.io.FileOutputStream;
- import java.io.IOException;
-
- public class Demo10_Chinese {
-
- public static void main(String[] args) throws IOException {
- //demo1();
- FileOutputStream fos = new FileOutputStream("zzz.txt");
- fos.write("我读书少,你不要骗我".getBytes());
- fos.write("\r\n".getBytes());
- fos.close();
- }
-
- public static void demo1() throws FileNotFoundException, IOException {
- FileInputStream fis = new FileInputStream("yyy.txt");
- byte[] arr = new byte[4];
- int len;
- while((len = fis.read(arr)) != -1) {
- System.out.println(new String(arr,0,len));
- }
- fis.close();
- }
- }
继承关系:
只用于字符转字节,写入字节文件。
OutputStreamWriter:将字符编码为字节,再写入字节文件。
刷新后还能继续写数据,关闭后不能再写数据。
只用于从字节文件解码出字符。
InputStreamReader:读取字节文件,并将字节解码为字符。构造函数传字节输入流FileInputStream或输入流InputStream.
编码:通过OutputStreamWriter将字符编码为字节,再写入字节文件。
解码:通过InputStreamReader读取字节文件,并将字节解码为字符
编码解码方法:
编码:字符文件转字节文件
解码:字节文件转字符文件
先写再读:
- OutputStreamWriter osw=new OutputStreamWriter(new FileOutputStream("1.txt"),"utf-8");
- osw.write("你好hello");
- osw.close(); //close很重要
- InputStreamReader isr=new InputStreamReader(new FileInputStream("1.txt"),"utf-8");
- int ch;
- while ((ch=isr.read())!=-1){
- System.out.println((char) ch);
- }
- // char []c=new char[1024];int len;
- // while ((len=isr.read(c))!=-1){
- // System.out.println(new String(c,0,len));
- // }
- isr.close();
字符输入输出流的子类,简化他们。涉及到编码解码问题,还是要使用字符输入输出流。
FileWriter(String fileName) 构造一个给定文件名的FileWriter对象。 |
FileReader(String fileName) 创建一个新的 FileReader ,给定要读取的文件的名称。 |
BufferedWriter
可以指定缓冲区大小,或者可以接受默认大小。 默认值足够大,可用于大多数用途。
提供了一个newLine()方法,它使用平台自己的系统属性line.separator定义的行分隔符概念。
每次写完bw.flush();可以将缓冲字符写入文本,虽然close()也能刷新后关闭,但加上flush更保险。
BufferedWriter(Writer out) 创建使用默认大小的输出缓冲区的缓冲字符输出流。 |
BufferedReader
从字符输入流读取文本,缓冲字符,以提供字符,数组和行的高效读取。
可以指定缓冲区大小,或者可以使用默认大小。 默认值足够大,可用于大多数用途。
BufferedReader实现读取键盘录入:
BufferedReader(Reader in) 创建使用默认大小的输入缓冲区的缓冲字符输入流 |
- //文本复制
- import java.io.*;
- import java.nio.charset.StandardCharsets;
- import java.util.Arrays;
-
- public class Demo {
- public static void main(String[] args) throws IOException {
- BufferedWriter bw=new BufferedWriter(new FileWriter("1//copy.txt"));
- BufferedReader br=new BufferedReader(new FileReader("1.txt"));
- char[] c=new char[1024];
- int len;
- while((len=br.read(c))!=-1){
- bw.write(c,0,len);bw.flush();
- }
- bw.close();br.close();
- }
- }
或者
- BufferedWriter bw=new BufferedWriter(new FileWriter("1//copy.txt"));
- BufferedReader br=new BufferedReader(new FileReader("1.txt"));
- String line;
- while ((line=br.readLine())!=null){
- bw.write(line);bw.newLine();bw.flush();
- }
- bw.close();br.close();
- //文本复制JDK7异常处理
- import java.io.*;
- import java.nio.charset.StandardCharsets;
- import java.util.Arrays;
-
- public class Demo {
- public static void main(String[] args){
- try(BufferedWriter bw=new BufferedWriter(new FileWriter("1\\2\\copy.txt"));
- BufferedReader br=new BufferedReader(new FileReader("1.txt"));){
- char[] c=new char[1024];
- int len;
- while((len=br.read(c))!=-1){
- bw.write(c,0,len);
- }
- // bw.close();br.close();不用close了,JDK7自动释放资源
- }catch (IOException e){
- e.printStackTrace();
- }
-
- }
- }
标准输出流:System.out
标准输入流:System.in
static PrintStream | err “标准”错误输出流。 |
static InputStream | in “标准”输入流。返回输入字节流 |
static PrintStream | out “标准”输出流。返回字节打印流 |
- PrintStream ps=System.out;
- ps.println("hello");
只负责输出数据,不负责读取数据。
- PrintStream pw=new PrintStream("1.txt");
- pw.println("hello");
- pw.println(23);
- pw.close();
- PrintWriter pw=new PrintWriter("1.txt",true);//自动刷新true
- pw.println("hello"); //直接写一行加刷新
- pw.println(23);
ObjectOutputStream
ObjectOutputStream(OutputStream out) 创建一个写入指定的OutputStream的ObjectOutputStream。 |
对象序列化方法:
void | writeObject(Object obj) 将指定的对象写入ObjectOutputStream。 |
只有支持java.io.Serializable接口的对象才能写入流中,将类实现Serializable接口即可,该接口是标识接口,不需要实现方法。
异常java.io.InvalidClassException当序列化运行时检测到类中的以下问题之一时抛出。
强烈建议所有可序列化的类都明确声明serialVersionUID值,因为默认的serialVersionUID计算对类详细信息非常敏感,从而出现InvalidClassException。
为了保证不同Java编译器实现之间的一致的serialVersionUID值,一个可序列化的类必须声明一个显式的serialVersionUID值,尽量使用private修饰符。
被transient修饰的成员变量不参与序列化。
ObjectInputStream(InputStream in) 创建从指定的InputStream读取的ObjectInputStream。 |
反序列化方法:
Object | readObject() 从ObjectInputStream读取一个对象。 |
Properties类虽然是Map的子类,但它不是泛型类,put方法添加键值时,键值都是Object类型的:
用String类型键值对要用特有方法:
String | getProperty(String key) 使用此属性列表中指定的键搜索属性。 |
Object | setProperty(String key, String value) 添加字符串类型键值对。 |
Set<String> | stringPropertyNames() 返回此属性列表中的一组键,其中键及其对应的值为字符串,包括默认属性列表中的不同键,如果尚未从主属性列表中找到相同名称的键。 |
void | load(InputStream inStream) 从输入字节流读取属性列表(键和元素对)。 |
void | load(Reader reader) 以简单的线性格式从输入字符流读取属性列表(关键字和元素对)。 |
void | store(OutputStream out, String comments) 将此属性列表(键和元素对)写入此 |
void | store(Writer writer, String comments) 将此属性列表(键和元素对)写入此 |
遍历:
与IO流结合,写入配置文件并读
- Properties prop=new Properties();
- prop.setProperty("a","b");prop.setProperty("c","d");
- BufferedWriter bw=new BufferedWriter(new FileWriter("1.txt"));
- prop.store(bw,"hello");
- bw.close();
-
-
- Properties p2=new Properties();
- BufferedReader br = new BufferedReader(new FileReader("1.txt"));
- p2.load(br);
- Set<String> s=p2.stringPropertyNames();
- for(String key:s){
- System.out.println(key+p2.getProperty(key));
进程:一个进程包括由操作系统分配的内存空间,包含一个或多个线程。
一个线程不能独立的存在,它必须是进程的一部分。一个进程一直运行,直到所有的非守护线程都结束运行后才能结束。
线程:一条线程指的是进程中一个单一顺序的控制流,是一条执行路径。
一个进程中可以并发多个线程,每条线程并行执行不同的任务。
单线程:一条进程里只有一条执行路径。
多线程:一个进程里有多条执行路径。
java是抢占式调度模型
优先级:每一个 Java 线程都有一个优先级,优先级是一个整数,其取值范围是 1 (Thread.MIN_PRIORITY ) - 10 (Thread.MAX_PRIORITY )。
默认情况下,每一个线程都会分配一个优先级 NORM_PRIORITY(5)。
优先级高的线程只是获取CPU时间片的几率高,并不能保证先执行。
创建线程方法:
run()和start()区别:
run():封装线程执行的代码,直接调用相当于普通方法的调用。
start():启动线程,虚拟机调用该线程的run()方法。
示例:
- public class Demo {
- public static void main(String[] args){
- MyThread a=new MyThread("a"),b=new MyThread("b");
- a.start();b.start();
- }
- }
-
- public class MyThread extends Thread{
- @Override
- public void run(){
- for(int i=0;i<100;i++) System.out.println(getName()+":"+i);
- }
-
- public MyThread() {
- }
-
- public MyThread(String name) {
- super(name);
- }
- }
运行结果:
Thead类常用方法:
String | getName() 返回此线程的名称。 |
void | setName(String name) 将此线程的名称更改为等于参数 |
static Thread | currentThread() 返回对当前正在执行的线程对象的引用。 |
int | getPriority() 返回此线程的优先级。 |
void | setPriority(int newPriority) 更改此线程的优先级。 |
static void | sleep(long millis) 使当前正在执行的线程以指定的毫秒数暂停(暂时停止执行),具体取决于系统定时器和调度程序的精度和准确性。 |
void | join() 等待这个线程死亡。 |
void | setDaemon(boolean on) 将此线程标记为守护线程,当只剩守护线程运行时,java虚拟机将退出。 |
主线程设置名字:
- Thread.currentThread().setName("主线程");
- System.out.println(Thread.currentThread().getName());
Runnable翻译:可运行的
步骤:
这种办法更好,优点:
(1)避免由于 Java 单继承带来的局限性
(2)逻辑和数据更好分离。通过实现 Runnable 接口的方法创建多线程更加适合同一个资源被多段业务逻辑并行处理的场景。在同一个资源被多个线程逻辑异步、并行处理的场景中,通过实现 Runnable 接口的方式设计多个 target 执行目标类可以更加方便、清晰地将执行逻辑和数据存储分离,更好地体现了面向对象的设计思想
示例:
- public class Demo {
- public static void main(String[] args){
- MyRunnable mr=new MyRunnable();
- Thread a=new Thread(mr,"a"),b=new Thread(mr,"b"),c=new Thread(mr);
- a.start();b.start();c.start();
- }
- }
-
- public class MyRunnable implements Runnable{
- @Override
- public void run(){
- for(int i=0;i<100;i++){
- System.out.println(Thread.currentThread().getName()+":"+i);
- }
- }
- }
运行结果:
通过Lambda表达式实现Runnable接口来创建线程
package train; public class Demo { public static void main(String[] args) { new Thread(()->{ System.out.println("hello"); }).start(); new Thread(()->{ Thread.currentThread().setName("a"); for(int i=0;i<100;i++) System.out.println(Thread.currentThread().getName()+":"+i); }).start(); new Thread(()->{ Thread.currentThread().setName("b"); for(int i=0;i<100;i++) System.out.println(Thread.currentThread().getName()+":"+i); }).start(); } }
多条语句共享数据时,多线程程序会出现数据安全问题。
同步代码块简单来说就是将一段代码用一把锁给锁起来, 只有获得了这把锁的线程才访问, 并且同一时刻, 只有一个线程能持有这把锁, 这样就保证了同一时刻只有一个线程能执行被锁住的代码.
- synchronized(同步对象) {
- //多条语句操作共享数据的代码
- }
同步代码块的好处:解决了多线程的数据安全问题
弊端:线程很多时,每个线程都会去判断锁,这是很耗费资源和时间的。
非静态同步方法的锁对象为this。下面代码是相同功能的同步方法和同步代码块:
静态同步方法的锁对象为:类名.class。下面代码是相同功能的同步方法和同步代码块:
StringBuffer,Vector,Hashtable
若不考虑线程安全,分别替换为StringBuilder,ArrayList,HashMap.
Lock提供比同步方法和代码块更广泛的锁定操作。
Lock是接口,ReentrantLock是它的实现类。
一个十分经典的多线程协作模式。
Object类的等待和唤醒方法:
void | wait() 导致当前线程等待,直到另一个线程调用该对象的 |
void | notify() 唤醒正在等待对象监视器的单个线程。 |
void | notifyAll() 唤醒正在等待对象监视器的所有线程。 |
计算机网络:计算机网络是指将地理位置不同的具有独立功能的多台计算机及其外部设备,通过通信线路连接起来,在网络操作系统,网络管理软件及网络通信协议的管理和协调下,实现资源共享和信息传递的计算机系统。
网络编程:指编写运行在多个设备(计算机)的程序,这些设备都通过网络连接起来。
查看主机名和ip地址:控制面板--系统和安全-系统。
常用命令:
ipconfig(查看计算机IP地址)
ping IP地址(测试连通性)
特殊IP地址:192.0.0.1是回送地址,可以代表本机地址,一般用来测试使用。
这个类表示互联网协议(IP)地址。没有构造方法,
static InetAddress | getByName(String host) 确定主机名称的IP地址。主机名称可以是ip地址,也可以是机器名称。 |
String | getHostAddress() 返回文本显示中的IP地址字符串。 |
String | getHostName() 获取此IP地址的主机名。 |
- InetAddress administrator = InetAddress.getByName("diannaomingcheng");
- System.out.println(administrator.getHostAddress());
- System.out.println(administrator.getHostName());
概念:
发送数据的步骤:
接收数据的步骤:
DatagramSocket构造方法:
| DatagramSocket() 构造数据报套接字并将其绑定到本地主机上的任何可用端口。 |
| DatagramSocket(int port) 构造数据报套接字并将其绑定到本地主机上的指定端口。 |
一般方法:
void | receive(DatagramPacket p) 从此套接字接收数据报包。 |
void | send(DatagramPacket p) 从此套接字发送数据报包。 |
void | close() 关闭此数据报套接字。 |
DatagramPacket构造方法:
DatagramPacket(byte[] buf, int length, InetAddress address, int port) 构造用于发送长度的分组的数据报包 |
DatagramPacket(byte[] buf, int length) 构造一个 |
一般方法:
发送接收数据代码:
- package udp_package;
- //发送数据
- import java.io.IOException;
- import java.net.DatagramPacket;
- import java.net.DatagramSocket;
- import java.net.InetAddress;
- import java.nio.charset.StandardCharsets;
-
- public class SendDemo {
- public static void main(String[] args) throws IOException {
- InetAddress ia = InetAddress.getByName("zhujiming");
- DatagramSocket ds=new DatagramSocket();
- byte []b="hello世界".getBytes();
- DatagramPacket dp=new DatagramPacket(b,b.length,ia,1234);
- ds.send(dp);
- ds.close();
- }
- }
- package udp_package;
- //接受数据
- import java.io.IOException;
- import java.net.DatagramPacket;
- import java.net.DatagramSocket;
- import java.net.SocketException;
-
- public class ReceiveDemo {
- public static void main(String[] args) throws IOException {
- DatagramSocket ds=new DatagramSocket(1234);
- byte []b=new byte[1024];
- DatagramPacket dp=new DatagramPacket(b,1024);
- ds.receive(dp);
- System.out.println("接受到的数据:"+new String(dp.getData(),0,dp.getLength()));
- }
- }
先运行接收,再运行发送。
客户端发送数据,服务端接受数据。
套接字是两台机器之间通讯的端点。
客户端套接字类Socket方法:
Socket(String host, int port) 创建流套接字并将其连接到指定主机上的指定端口号。 |
Socket(InetAddress address, int port) 创建流套接字并将其连接到指定IP地址的指定端口号。 |
一般方法:
OutputStream | getOutputStream() 返回此套接字的输出流。 |
InputStream | getInputStream() 返回此套接字的输入流。 |
void | shutdownOutput() 禁用此套接字的输出流。 |
OutputStream类是所有输出字节流的超类:
OutputStream封装到BufferedWriter:
字节输出流OutputStream转字符输出流OutputStreamWriter转缓冲字符输出流BufferedWriter。
这里s是Socket对象:
- package tcp_package;
- //客户端发送数据
-
- import java.io.IOException;
- import java.io.OutputStream;
- import java.net.InetAddress;
- import java.net.Socket;
- import java.net.UnknownHostException;
-
- public class SendDemo {
- public static void main(String[] args) throws IOException {
- InetAddress ia=InetAddress.getByName("mingzi");
- Socket s=new Socket(ia,1234);
- OutputStream os=s.getOutputStream(); //这里就是IO流了,可以把字节输出流OutputStream转为缓冲字符输出流BufferedWriter,从而发送字符
- os.write("hello世界".getBytes());
- s.close();
- }
- }
所谓套接字(Socket),就是对网络中不同主机上的应用进程之间进行双向通信的端点的抽象。
服务器套接字类ServerSocker方法:
ServerSocket(int port) 创建绑定到指定端口的服务器套接字。 |
..
InputStream是所有输入字节流类的超类:
字节输入流InputStream封装到字符缓冲输入流BufferedWriter:
这里s是Socket对象:
这样就能每次读取一行字符串,更方便。
代码
- package tcp_package;
- //服务端接受数据
-
- import java.io.IOException;
- import java.io.InputStream;
- import java.net.ServerSocket;
- import java.net.Socket;
-
- public class ReceiveDemo {
- public static void main(String[] args) throws IOException {
- ServerSocket ss=new ServerSocket(1234);
- Socket s=ss.accept();
- InputStream is=s.getInputStream();
- byte[] b=new byte[1024];
- int len=is.read(b);
- System.out.println("data is:"+new String(b,0,len));
- ss.close(); //关ss就行了,因为s是从ss得到的。
- }
- }
- package tcp_package;
-
- import java.io.*;
- import java.net.Socket;
-
- public class ClientDemo{
- public static void main(String[] args) throws IOException {
- Socket s=new Socket("xx.xx.xx.xx",1234);
- BufferedWriter bw=new BufferedWriter(new OutputStreamWriter(s.getOutputStream()));
- BufferedReader br=new BufferedReader(new InputStreamReader(System.in));
- String line;
- while ((line=br.readLine())!=null){
- if(line.equals("886")) break;
- bw.write(line);
- bw.newLine();
- bw.flush(); //缓冲流别忘了刷新,不然发不出去
- }
- s.shutdowmOutput();
- //shutdowmOutput()代表停止发送数据,此处可加BufferedReader反馈代码,接收服务端传输成功的反馈
- s.close(); //s是bw的源头,关s就关了bw
- br.close();
- }
-
-
-
- }
- package tcp_package;
-
- import java.io.BufferedReader;
- import java.io.IOException;
- import java.io.InputStreamReader;
- import java.net.ServerSocket;
- import java.net.Socket;
-
- public class ServerDemo {
- public static void main(String[] args) throws IOException {
- ServerSocket ss=new ServerSocket(1234);
- Socket s=ss.accept();
- BufferedReader br=new BufferedReader(new InputStreamReader(s.getInputStream()));
- String line;
- while((line=br.readLine())!=null){
- System.out.println(line);
- }
- ss.close();
- }
- }
Lambda表达式是一个对象,必须有上下文环境,作用是实现单方法的接口。
上下文环境意思是能证明它是对象,例如让它处在方法或类的实参里,或者赋值给对象引用。
注意事项:
Lambda表达式格式:
(形参)->{方法内的代码块}
可省略内容:
- public class Demo {
- public static void main(String[] args) {
- fun((a,b)->{ //Lambda形参类型、返回类型可以省略
- return a+b;
- });
- }
- public static void fun(Dog d){
- d.add(1,4);
- }
- }
-
- public interface Dog { //单方法接口
- public int add(int a,int b);//只能有一个方法
- }
单参数单语句省略示例:
- public class Test {
- public static void main(String[] args) {
- //Lambda表达式简化单方法接口实现类对象的创建过程
- fun((a,b)->{
- return a+b;
- });
- //如果不用Lambda表达式,则要用下面的方式调用fun(Dog d)方法
- fun(new Dog() {
- @Override
- public int add(int a, int b) {
- return a+b;
- }
- });
- }
- public static void fun(Dog d){
- d.add(1,4);
- }
- }
- new Thread(()->{ //虚拟机根据上下文能推断出实现的接口是Runnable
- Thread.currentThread().setName("a");
- for(int i=0;i<100;i++) System.out.println(Thread.currentThread().getName()+":"+i);
- }).start();
可见Lambda表达式方法最简洁,匿名内部类名和方法名都省了。
java8后加入了默认方法、静态方法。java9后加入了私有方法。
默认方法可被子类对象调用。
多态复习:接口不能初始化对象,只能接口引用指向实现类实例化对象,这时引用只能调用接口方法,实际执行的是实现类实现的对应方法。
默认方法不是抽象方法,实现类可以重写,也可以不重写。
- public interface Dog {
- public default void eat(){
- System.out.println("he");
- }
- }
接口中静态方法只能被接口名调用,不能被实现类对象调用。
假如类A实现了有同名静态方法的接口B、C,则A的对象分不清调用哪个接口的方法。
- public interface Dog {
- public static void eat(){
- System.out.println("he");
- }
- }
-
- public class Demo {
- public static void main(String[] args) {
- Dog.eat();
-
- }
- }
起源:为了将接口中多个默认或静态方法中的共性方法抽离出来。
修饰符复习:私有方法权限最低,仅同类内可见。静态方法只能访问静态变量和方法,不能用this。
- public interface Dog {
- public static void eat(){ //静态方法只能访问静态变量和方法
- look();
- }
- public default void sleep(){ //非静态方法可以访问静态非静态成员
- look();
- }
- private static void look(){ //私有成员仅本类内可见
- System.out.println("look");
- }
- }
方法引用和Lambda都可以根据上下文推导。
格式:
引用类::不带括号的方法名
引用Lambda中类的方法:
- public class Demo {
- public static void main(String[] args) {
- fun((s)-> System.out.println(s)); //fruit
- fun(System.out::println); //fruit //根据上下文推导出标准输出流类的println方法
- }
- public static void fun(Dog dog){ //Dog是个接口
- dog.eat(" fruit");
- }
- }
引用Lambda中类的方法:
- fun((s)->Integer.parseInt(s)); //字符串转数字,
- fun(Integer::parseInt); //根据上下文推导Integer类的parseInt方法
引用匿名内部类中类的方法:
- fun(new Dog() {
- @Override
- public void eat(String s) {
- System.out.println(s);
- }
- });
- fun(System.out::println);
- public class Demo {
- public static void main(String[] args) {
- Dog dog=new Dog(); //Dog是类
- fun(dog::eat); 引用对象的方法,实参为对象,形参是接口
- }
- public static void fun(Cat c){ //Cat是接口
- c.look("4346");
- }
- }
函数式接口:有且仅有一个抽象方法的接口。
Java中函数式编程体现就是Lambda表达式。
函数式接口注解:@FunctionalInterface
如果方法的返回值是函数式接口,可以使用Lambda表达式作为结果返回。
格式: 函数式接口对象作为一个方法的形参,实参是Lambda表达式。
事实上,JDK自带一些函数式接口:函数式接口Supplier中get获取有返回值的数据,Predicate中test返回boolean型数据,Consumer中accept无返回值数据操作。
接口 | 方法 | 作用 | 示例 |
Supplier | get,返回各类型的数据 | 选择 | 返回数组最大值 |
Function | apply,返回各类型数据 | 转换 | 数字字符串转换 |
Predicate | test,返回boolean型数据 | 判断 | 判断数字是否大于5 |
Consumer | accept,无返回值 | 处理 | 翻转字符串 |
@FunctionalInterface public interface Supplier<T>
作用:获取到生产出的数据
Supplier译作供应商,生产的数据类型由泛型指定。
- public class Test {
- public static void main(String[] args) {
- // 定义一个int数组
- int[] arr = {19, 50, 28, 37, 46};
- // 调用getMax方法,方法的形参是函数式接口(单方法接口),
- // 实参Lambda表达式(接口方法的逻辑,相当于创建了一个Supplier对象)
- // 返回值是Lambda表达式的返回值,sup.get()
- int maxValue = getMax(() -> {
- int max = arr[0];
- for (int i = 1; i < arr.length; i++) {
- if (arr[i] > max) {
- max = arr[i];
- }
- }
- return max;
- });
- System.out.println(maxValue);
- }
-
- /**
- * 返回一个int数组中的最大值
- * @param sup 生产者,是个函数式接口,实参是Lambda表达式(接口方法的逻辑,相当于一个Supplier对象)
- * @return int 最大值
- */
- private static int getMax(Supplier<Integer> sup) {
- return sup.get();
- }
- }
@FunctionalInterface public interface Consumer<T>
作用:对数据进行操作,无返回值。
Consumer是消费型接口,消费的数据类型由泛型指定。
a.andThen(b).accept(t);相当于a.accept(t);b.accept(t);
@FunctionalInterface public interface Predicate<T>
作用:判断参数是否满足条件。
译作谓词,谓语。
方法的形参Predicate对象是Lambda参数。
- public class Demo {
- public static void main(String[] args) {
- boolean b = fun("hello", s -> s.length() > 5, s -> s.length() <100);
- }
-
- private static boolean fun(String s, Predicate<String> p1, Predicate<String> p2) {
- return p1.and(p2).test(s);
- }
- private static boolean fun(String s, Predicate<String> p) {
- return p.negate().test(s);
- }
- }
@FunctionalInterface public interface Function<T,R>
作用:用于对参数进行处理、转换,然后返回一个新值。
- public class Demo {
- public static void main(String[] args) {
- String s="小明,33";
- fun(s,s1->s1.split(",")[1],s1->Integer.parseInt(s1),i->i+70);
- }
- //先分割,再转数字,再加70
- private static void fun(String s, Function<String,String> fun1,Function<String,Integer> fun2,Function<Integer,Integer> fun3){
-
- int i=fun1.andThen(fun2).andThen(fun3).apply(s);//fun1的结果给fun2,fun2的结果给fun3
- System.out.println(i);
- }
- }
Stream流真正把函数式编程风格引用java中。
Stream流过程:
filter()和forEach()参数都是Lambda或方法引用。
代码:
Stream<T> | filter(Predicate<? super T> predicate) 返回由与此给定谓词匹配的此流的元素组成的流。 |
Stream<T> | limit(long maxSize) 返回由此流的元素组成的流,截短长度不能超过 |
Stream<T> | skip(long n) 在丢弃流的第一个 |
static <T> Stream<T> | concat(Stream<? extends T> a, Stream<? extends T> b) 创建一个懒惰连接的流,其元素是第一个流的所有元素,后跟第二个流的所有元素。 |
Stream<T> | distinct() 返回由该流的不同元素(根据 |
Stream<T> | sorted() 返回由此流的元素组成的流,根据自然顺序排序。 |
Stream<T> | sorted(Comparator<? super T> comparator) 返回由该流的元素组成的流,根据提供的 |
<R> Stream<R> | map(Function<? super T,? extends R> mapper) 返回由给定函数应用于此流的元素的结果组成的流。 |
IntStream | mapToInt(ToIntFunction<? super T> mapper) 返回一个 |
filter
filter 方法用于通过设置的条件过滤出元素。以下代码片段使用 filter 方法过滤掉空字符串,打印非空字符串。filter的参数是Lambda,Lambda参数是list的每个元素,返回值是Boolean,为true时留下
- List<String>list = Arrays.asList("abc", "", "bc", "efg", "abcd","", "jkl");
- //forEach终结后,原list不变,处理后的list遍历输出
- list.stream().filter(item -> !item.isEmpty()).forEach(System.out::println);
skip&limit
concat和distinct
sorted比较器
sorted()
list.stream().sorted(参数是Lambda,Lambda参数是list的两个元素,返回值>0是升序,返回值<0是降序)
先按字母序再按长度排序:
- List<String> list = Arrays.asList("abc", "", "bc", "efg", "abcd","", "jkl");
- //forEach终结后,原list不变,处理后的list遍历输出
- list=list.stream().sorted((item1,item2)->{
- if(item1.compareTo(item2)!=0) return item1.compareTo(item2);
- else return item1.length()-item2.length();
- }).collect(Collectors.toList());
- System.out.println(list);
- }
输出:[, , abc, abcd, bc, efg, jkl]
map
中间操作map()用法和filter类似,map的参数是Lambda,Lambda参数是list的每个元素,返回值是加工后的list元素。
- List<String>list = Arrays.asList("abc", "", "bc", "efg", "abcd","", "jkl");
- //collect收集后原list不变,处理后的list收集成返回值
- list=list.stream().map(item->item.toUpperCase()).collect(Collectors.toList());
- System.out.println(list);
mapToInt
IntStream是由Integer元素组成的流,可以取和、平均数、排序、skip、limit、conunt、forEach等。
filter是满足条件的留下,是对原数组的过滤;map则是对原数组的加工,映射成一对一映射的新数组。
说人话就是改变了了长度了用filter,没改变长度,只是对某一个或者几个做改变用map
long | count() 返回此流中的元素数 |
void | forEach(IntConsumer action) 对此流的每个元素执行操作。 |
<R,A> R | collect(Collector<? super T,A,R> collector) 使用 Collector对此流的元素执行 mutable reduction |
参数Collector对象通过工具类Collections返回值得到:
示例:
类加载器ClassLoader
ClassLoader:负责加载类的对象。
内置类加载器:
ClassLoader的方法:
static ClassLoader | getSystemClassLoader() 返回用于委派的系统类加载器。 |
ClassLoader | getParent() 返回父类加载器进行委派。 |
反射就是把Java类中的各个成分映射成一个个的Java对象。
即在运行状态中,对于任意一个类,都能够知道这个类的所以属性和方法;对于任意一个对象,都能调用它的任意一个方法和属性。这种动态获取信息及动态调用对象方法的功能叫Java的反射机制。
反射机制有什么用?
通过java语言中的反射机制可以操作字节码文件(可以读和修改字节码文件,即xxx.class文件)
通过反射机制可以操作代码片段。(class文件。)
构造方法、成员变量、方法对象取消访问检查可以访问私有成员:
反射可以越过泛型检查,例如在ArrayList<Integer>中添加字符串:
Class类的对象:该类的字节码文件对象。
同一个类的字节码文件对象永远是相等的。
- public static void main(String[] args) throws ClassNotFoundException {
- Class<Dog> c1=Dog.class; //方法1:类的class属性
- Dog d=new Dog();
- Class<? extends Dog> c2=d.getClass(); //方法2:对象的getClass方法
- Class<?> c3= Class.forName("train.Dog"); //方法3:Class类的静态方法forName
- System.out.println(c1); //class train.Dog
- System.out.println(c1==c2&&c1==c3); //true
- }
Class类的引用也可以不加泛型:
- Class c1=Dog.class;
- Class c2=d.getClass();
- Class c3= Class.forName("train.Dog");
Class类的方法:
Constructor<T> | getConstructor(类<?>... parameterTypes) 返回单个公共构造方法对象。 |
Constructor<T> | getDeclaredConstructor(类<?>... parameterTypes) 返回单个构造方法对象。 |
Constructor<?>[] | getConstructors() 返回所有公共构造方法对象的数组。 |
Constructor<?>[] | getDeclaredConstructors() 返回所有构造方法对象的数组。 |
T | newInstance() 调用类的指定构造方法,完成构造方法的对象创建。 |
Constructor译作构造方法,构造器。
Class.newInstance() 只能够调用无参的构造函数,即默认的构造函数;
Constructor.newInstance() 可以根据传入的参数,调用任意构造函数。
构造方法、成员变量、方法对象取消访问检查可以访问私有成员:
获取所有构造方法的对象:
- public static void main(String[] args) throws ClassNotFoundException {
- Class<?> c= Class.forName("train.Dog");
- Constructor<?>[] cons = c.getDeclaredConstructors();
- for(Constructor<?> con:cons){
- System.out.println(con);
- }
- }
获取单个构造方法的对象:
无参:
- Class<?> c= Class.forName("train.Dog");
- Constructor<?> con=c.getDeclaredConstructor(); //获取单个构造方法对象
- Object obj = con.newInstance(); //构造方法对象实例化,会调用无参构造方法
- System.out.println(obj); //重写Dog类的to_String方法可更直观。
带参:
- Class<?> c= Class.forName("train.Dog");
- Constructor<?> con=c.getConstructor(String.class,int.class);
- Object obj = con.newInstance("wangcai",32);
- System.out.println(obj);
Class对象获取成员变量的方法:
Field | getField(String name) 返回一个 |
Field[] | getFields() 返回包含一个数组 |
Field | getDeclaredField(String name) 返回一个 |
Field[] | getDeclaredFields() 返回的数组 |
Field是字段,成员变量属于字段。
Field类的方法:
获取成员变量对象并赋值:
- // 1.获取构造方法对象并实例化
- Dog dog=new Dog();
- Class<? extends Dog> c = dog.getClass();
- Constructor<?> con=c.getConstructor();
- Object obj = con.newInstance();
- // 2.获取成员变量对象
- Field field = c.getField("name");
- // 3.通过成员变量对象的方法set,给构造方法对象赋值
- field.set(obj,"旺财");
- System.out.println(obj); //Dog{age=0, name='旺财'}
- System.out.println(dog.name); //null
Class对象获取成员方法的方法:
Method | getMethod(String name, 类<?>... parameterTypes) 返回一个 |
Method[] | getMethods() 返回包含一个数组 |
Method | getDeclaredMethod(String name, 类<?>... parameterTypes) 返回一个 |
Method[] | getDeclaredMethods() 返回包含一个数组 |
Method类的方法:
获取成员变量对象并调用:
- // 1.获取构造方法对象并实例化
- Class<?> c= Class.forName("train.Dog");
- Constructor<?> con=c.getConstructor();
- Object obj = con.newInstance();
- // 2.获取成员方法对象
- Method eat = c.getMethod("eat");
- // 3.通过成员方法对象的invoke方法,调用构造方法对象的成员方法
- //无参无返回值方法
- eat.invoke(obj);
- //带参有返回值方法
- Object sucess= eat.invoke(obj,"food");
- System.out.println((boolean)sucess);
Properties类在特殊操作流那里有细讲。
示例:
特点和使用场景
文档声明:
标签(元素)规则跟HTML类似:
注释、特殊字符、CDATA区:
CDATA区可以任意写文本而不用担心冲突的发生,输入CD加回车能快速打出本语句。
文档约束的分类:DTD和schema
DTD约束步骤
示例:
作用和问题:
schema约束:
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。