-
以
.java
为后缀的文件为java程序的源文件,以.class
结尾的文件为编译后的文件。在使用控制台运行java程序的时候,需要先使用javac
命令编译源文件成.class
文件,再使用java
命令运行编译后的文件。需要注意的是编译时文件名要带后缀,运行时不带后缀。 -
JDK
称为Java开发工具包,JRE
称为Java运行环境,JVM
称为Java虚拟机。JDK包含JRE,JRE包含jvm。 -
Java语言之所以被称之为跨平台语言得益于有Java虚拟机, java源程序(.java文件)通过编译器编译成为Class文件(字节码文件),而它的class文件是基于字节码(以byte为单位存储的文件)的,而字节码文件是描述程序要运行的的虚指令的集合,这些虚指令的集合与任何的平台无关,Java虚拟机认识它(只要在不同的平台下部署相应的jre,运行jvm!就可以了)。
-
Java语言的特点:
- 简单性
- 面向对象
- 分布性
- 编译和解释性
- 稳健性
- 安全性
- 可移植性
- 高性能
- 多线程性
- 动态性
-
Java的技术体系:
J2SE(Java 2 Platform Standard Edition)标准版
- 是为开发普通桌面和商务应用程序提供的解决方案,该技术体系是其他两者的基础,可以完成一些桌面应用程序的开发。
J2ME(Java 2 Platform Micro Edition)小型版
- 是为开发移动设备和嵌入式设备(电器、机器人、机顶盒…)提供的解决方案。
J2EE(Java 2 Platform Enterprise Edition)企业版
- 是为开发企业环境下的应用程序提供的一套解决方案,该技术体系中包含的技术如 Servlet、Jsp等,主要针对于Web应用程序开发。
Java FX
Java Card
Java TV
Java DB
-
八大基本数据类型
类型名称 字节空间 byte 1 short 2 int 4 long 8 float 4 double 8 char 2 boolean 1 -
基本数据类型的字面量:整数为int,小数为double。
-
若要用long类型接收超出int类型范围的整数,需要在整数后面加上后缀L 若要用float类型接收float类型范围内的小数,需要在小数后面加上后缀f 若要用double类型接收double范围内的整数,需要在整数后面加后缀d 这里的后缀不区分大小写。
-
隐式转换(小到大、自动转换)示意图: ![image-20200801224854467](E:\Markdown\Java 第一阶段笔记.assets\image-20200801224854467.png)
-
显示转换和隐式转换的区别:小类型转化为大类型系统自动转,大类型转化为小类型强制转换。
-
==变量的四则运算会发生数据类型的转换。==
byte a=1,b=1; byte c=a+b;//这里会发生数据类型的转换,会报错 byte d=a+1;//这里也会发生数据类型的转换,也会报错 byte e=1+1;//这里不会发生数据类型的转换,不会报错 final byte f=1,g=1; byte h=f+g;//这里不会发生数据类型的转换,因为f,g为常量
除一般运算符以外,重点介绍以下几种运算符
-
&&和&:前者叫短路与,在if语句中,当前一个表达式为false是,不会再运算第二个表达式的结果,后者会全部运算。
-
||和|:与上面一样,前者是短路或,后者不会短路。&和|除了这两种用途外,同时还是按位运算符。
-
按位运算符:&(与)、|(或)、~(非)、^(异或)
-
&(与):将数据化为二进制形式后,相同位的两位都为1,那么结果为1,否则为0。
-
|(或):将数据化为二进制形式后,相同位只要有一位为1,那么结果为1,全为0则为0。
-
~(非):非1=0,非0=1。
-
^(异或):将数据化为二进制形式后,相同位的两位不相同,那么结果为1,相同则为0。
//用异或的方式,不用临时变量实现变量的值交换。 System.out.println("交换后:a="+a+",b="+b); int m=0; int n=1; System.out.println("交换前:m="+m+",n="+n); m=m^n; n=n^m;//n=n^m^n,根据异或的性质:a^b^a=b,则前面的表达式的结果是n=m m=m^n;//m^n^m,表达式的结果是m=n;完成了值的交换 System.out.println("交换后:m="+m+",n="+n);
//使用异或和移位运算符求整数的绝对值 int a=-5; int b=(a^(a>>31))-(a>>31);//右移31是因为只取符号位 System.out.println(b);
//与运算符判断一个数的奇偶性 int n=6; System.out.println((n&1)==1?"奇数":"偶数"); //奇数最低位肯定是1,而1的二进制最低位也是1,其他位都是0,所以所有奇数和1与运算结果肯定是1。
-
-
移位运算符:<< (左移)、>>(右移)、>>>(无符号右移)。
- 左移(<<):转为二进制后,各二进制位全部左移N位,高位丢弃,低位补0。正数可直接操作原码==对于负数,需将其转化为反码再转化为补码,对补码进行移位操作,再转化为反码,再转化为原码。==
- 右移(>>):转为二进制后,各二进制位全部右移N位,若值为正,则在高位插入 0,若值为负,则在高位插入 1。正数可直接操作原码==对于负数,需将其转化为反码再转化为补码,对补码进行移位操作,再转化为反码,再转化为原码。==
- 无符号右移(>>>):转为二进制后,各二进制位全部右移N位,无论正负,都在高位插入0。正数可直接操作原码==对于负数,需将其转化为反码再转化为补码,对补码进行移位操作,再转化为反码,再转化为原码。==
-
计算结果的数据类型,与最大类型保持一致
-
byte、short、char三种比int小的整数,运算时会先自动转换成int
-
用异或、乘法除法实现三个变量的数值交换
public static void main(String[] args) { // 使用乘法不引入临时变量交换三个变量的值 // a=b. b=c、c=a int a = 1; int b = 2; int c = 3; System.out.println("交换前:a=" + a + ",b=" + b + ",c=" + c); a = a * b * c; c = a / b / c;// a b = a / b / c;// c a = a / b / c;// b System.out.println("交换后:a=" + a + ",b=" + b + ",c=" + c); //使用异或的方法 a = 1; b = 2; c = 3; a = a ^ b ^ c; c = a ^ b ^ c; b = a ^ b ^ c; a = a ^ b ^ c; System.out.println("交换后:a=" + a + ",b=" + b + ",c=" + c); }
-
数据类型的字面值
public static void main(String[] args) { byte a=1; short b=2; int c=3; long d=4; char e=5; float f=6; double g=7.0; //定义时需要注意的是byte、short、char只能初始化为int类型的常量 //float初始化为整数时不用加f,但初始化为小数时必须加f //long类型存int范围内的整数不需要加L,超过int且在long范围内必须加L //byte aa=c;报错 //short bb=c;报错 //char ee=c;报错 long dd=c;//可以直接赋值int类型的变量给long }
-
自增自减:符号在前,先变化后使用。符号在后,先使用后变化
byte a=1; //在这种情况下a+=4和a=a+4是有区别的。 //a=a+4属于求和,会发生数据类型的自动自动转换 //a+=4属于复合赋值运算,所以不会发生错误 a+=4; //a=a+4;//会报错,因为这样赋值会发生数据类型的自动转换。 int b=1; b+=4; b=b+4;//这里不报错是因为b的类型本身就是int类型,不用转换。
-
switch语句:
-
创建数组
动态初始化和静态初始化
- 动态初始化:
int[] a = new int[5];
- 静态初始化:
int[] a = new int[]{1,2,3,4,5};
。int[] a={1,2,3,4,5};
创建数组有两种方式,三种方法(即上面的两种动态和静态方式,以及其中的三种方法)
- 动态初始化:
- 栈:存局部变量,先进先出
- 堆:存对象-new
Person p = new Person();//短短这行代码发生了很多事情
- 把Person.class文件加载进内存
- 在栈内存中,开辟空间,存放变量p
- 在堆内存中,开辟空间,存放Person对象
- 对成员变量进行默认的初始化
- 对成员变量进行显示初始化
- 执行构造方法(如果有构造代码块,就先执行构造代码块再执行构造方法)
- 堆内存完成
- 把堆内存的地址值赋值给变量p ,p就是一个引用变量,引用了Person对象的地址值
- 类默认提供无参构造方法
- 如果定义了有参构造方法,在需要调用其无参构造方法时,必须显式声明其无参构造方法
-
构造代码块
- 成员位置(类里,方法外)的代码块叫构造代码块
- 作用:抽取构造器的共性
- 触发节点:new
- new创建对象时执行构造器,若存在构造代码块,先执行构造代码块,再执行构造器
-
局部代码块
- 作用:控制变量的作用范围,出了局部代码块就不可以用了
- 触发节点:调用方法时触发,按顺序执行
-
静态代码块
- 一段被花括号包起来的代码,而且被static修饰
- 位置是在成员位置,也拥有static关键字的特点
- 静态代码块用来完成项目的初始化
- ==只加载一次==,在类第一次被加载时就跟着一起加载了(类的资源)
- ==静态区域里只能调用静态资源,普通区域可以任意调用==
-
代码块执行顺序:静态代码块(只加载一次)>(构造代码块)>(构造方法)>(局部代码块)
- 代表本类对象的一个引用对象(或者说,是本类的对象的引用)
- 用来完成构造方法间的相互调用(只能单向调用,双向会引起死循环)
- 在构造方法中使用this关键字必须放在第一行
使用场景:
- 当要在附近有局部变量情况下使用和局部变量同名的成员变量时
- 在构造器中调用其他构造器时
-
概述:Java继承是使用已存在的类的定义作为基础建立新类的技术,新类的定义可以增加新的数据或新的功能,也可以用父类的功能,但不能选择性地继承父类/超类/基类。
-
好处:提高父类代码的复用性,减少子类代码的编写,提高开发效率
-
特点:
- 使用extends关键字
- 相当于子类把父类的功能复制了一份
- java只支持单继承
- 继承可以传递(爷爷,儿子,孙子的关系)
- ==不能继承父类的私有成员==
- 继承多用于功能的修改,子类可以拥有父类的功能的同时,进行功能拓展
- 像是is a 的关系
- ==构造器不能被继承==
-
什么是OCP原则?
Open Closed Principle(开闭原则):
(1)对扩展是开放的 即当要扩展功能时,不必更改内部代码块,可以直接进行扩展; (2)对更改是封闭的 对模块进行扩展时,内部代码无须改动。
一个软件实体如类、模块和函数应该对扩展开放,对修改关闭。
简单来说就是对添加开放,对修改关闭。
当一个类写之后,我们对于类功能的增强应该基于当前类进行添加,而不是对当前类进行修改。
- 当在子类中想用父类的成员变量
- 子类和父类的变量名不同名时,可以省略super关键字
- 子类和父类的变量名相同时,必须使用super关键字
- super关键字相当于父类对象的引用
==Java共有53个关键字,其中包含2个保留字(goto,const)==
- 可以修饰成员变量、成员方法
- ==随着类的加载而加载,优先于对象加载==
- ==只加载一次==,就会一直存在,不用开辟新的空间
- ==全局唯一,全局共享==
- 静态只能调用静态,非静态可以随意调用
- ==static不能和this或者super共用,因为static比对象先加载==
- 让子类的重写权限变得可控.如果父类的某个方法不让子类重写,可以用final修饰变成最终方法
- 表示最终的意思.可以修饰类,成员变量,成员方法
- final 修饰的类是最终的类,不能被继承
- final 修饰的变量,是一个最终的变量,值不能被修改,称之为是常量
- final 修饰的方法,不能被重写
- 多态的前提是继承+重写
- 要有方法的重写
- 父类引用指向子类对象
- 多态中,方法声明用--父类的 ,方法体用--子类的(因为重写了)
- ==对于调用父类成员变量还是子类成员变量,只与变量的声明类型有关系,与实例化的类型没有关系。==
- ==而对于调用父类方法还是子类方法,只与变量的实例化的类型有关系,与声明的类型没有关系,当然是在方法被重写的情况下,如果没有重写,则调用的是父类的方法。==
- ==要求方法声明完全一致(返回值,方法名,参数列表)==
- ===要求子类的权限修饰符大于等于父类的权限修饰符==
- ==在子类的构造器中,默认就会存在super()——调用父类的无参构造器==
- ==如果父类没有无参构造器(声明含参构造器后没显式声明无参构造器),子类的所有构造器都无法使用super()。==
- 静态资源不存在重写,谁调用就是执行谁的功能
- ==重写是指子类对父类方法的重写,成员变量是不能重写==
异常体系结构图
graph LR
A(Throwable) -->B(Error)
B --> B1(VirtulMachineError)
B1 --> B11(StackOverFlowError)
B1 --> B12(OutOfMemoryError)
B --> B2(AWTError)
A --> C(Exception)
C --> C1(IOException)
C1 --> C11(EOFException)
C1 --> C12(FileNotFoundException)
C --> C2(RuntimeException)
C2 --> C21(ArithmeticException)
C2 --> C22(MissingResourceException)
C2 --> C23(ClassNotFoundException)
C2 --> C24(NullpointerException)
C2 --> C25(IllegalArgumentException)
C2 --> C26(ArrayIndexOutOfBoundsException)
C2 --> C27(UnkownTypeException)
-
异常提示的组成:异常的类型,异常的提示,异常的行号
-
Error:系统级错误,无法修复
-
Exception:程序bug,可修复
-
异常的处理方式:捕获(推荐)、抛出
-
捕获:把可能发生错误的代码包起来,尝试执行
try{ //需要捕获的代码 }catch(异常类型 异常名){ //处理方案 } //------------------------------------ try{ // 程序代码 }catch(异常类型1 异常的变量名1){ // 程序代码 }catch(异常类型2 异常的变量名2){ // 程序代码 }finally{//无论是否发生异常,以下的代码总被执行 //程序代码 }
-
抛出:在方法声明上加上throws 异常类型
mian() throws InoutMismatchException
-
==异常处理五个关键字==
关键字 解释 try
执行可能产生异常的代码 catch
捕获异常 finally
无论是否发生异常,代码总被执行 throw
抛出异常 throws
声明异常 -
throw
和throws
的区别:throw是语句抛出一个异常,一般是在代码块的内部,当程序现某种逻辑错误时由程序员主动抛出某种特定类型的异常 throws表示出现异常的一种可能性,并不一定会发生这些异常,throw则是抛出了异常,执行throw则一定抛出了某种异常。 1、Throw用于方法内部,Throws用于方法声明上 2、Throw后跟异常对象,Throws后跟异常类型 3、Throw后只能跟一个异常对象,Throws后可以一次声明多种异常类型 Throw: 作用在方法内,表示抛出具体异常,由方法体内的语句处理。 具体向外抛出的动作,所以它抛出的是一个异常实体类。若执行了Throw一定是抛出了某种异常。 Throws: 作用在方法的声明上,表示如果抛出异常,则由该方法的调用者来进行异常处理。 主要的声明这个方法会抛出会抛出某种类型的异常,让它的使用者知道捕获异常的类型。 出现异常是一种可能性,但不一定会发生异常。
-
修饰符的关系: | 修饰符 | 本类 | 同一个包中的类 | 子类 | 其他类 | | :-------------: | :------: | :------------: | :----------: | :----------: | | public | 可以访问 | 可以访问 | 可以访问 | 可以访问 | | protected | 可以访问 | 可以访问 | 可以访问 | ==不能访问== | | 默认(default) | 可以访问 | 可以访问 | ==不能访问== | ==不能访问== | | private | 可以访问 | ==不能访问== | ==不能访问== | ==不能访问== |
-
public
>protected
>default
>private
没有方法体的方法我们称之为抽象方法,含有抽象方法的类我们称之为抽象类。
- 特点:
- 通过java关键字abstract实现
- 可以修饰方法或者类,==不能修饰变量==
- 抽象类中可以没有抽象方法(由子类去实现)
- 如果类中有抽象方法,那该类必须定义为一个抽象类
- 子类继承了抽象类以后,要么还是一个抽象类,要么就把所有抽象方法都重写
- 多用于多态中
- 抽象类不可以被实例化
- ==抽象类也有构造方法,但是不能本身实例化==
- 那抽象类的构造函数有啥用?==一般用于给子类实例化==
- 既可以有变量,也可以有常量
- ==抽象类里,既可以有普通方法,有可以有抽象方法==
- 一个类如果继承了一个抽象类,要么重写所有的抽象方法,要么把自己也修饰成一个抽象类
- 可以通过多态的方式调用抽象类的成员变量和成员方法
- 抽象类里可以都是抽象方法,要求子类重写所有的抽象方法,否则就是一个抽象子类
- 抽象类里可以都是普通方法,目的是不让外界实例化
- 抽象类的方法到底是设计成普通方法还是抽象方法看具体的需求(方法体有没有必要存在)
==如果一个类不想被实例化,可以将构造器私有化,还可以将类修饰成抽象类==
Java里面由于不允许多重继承,所以如果要实现多个类的功能,则可以通过实现多个接口来实现。
Java接口和Java抽象类代表的就是抽象类型,就是我们需要提出的抽象层的具体表现。OOP面向对象的编程,如果要提高程序的复用率,增加程序的可维护性,可扩展性,就必须是面向接口的编程,面向抽象的编程,正确地使用接口、抽象类这些太有用的抽象类型做为java结构层次上的顶层。
interface 接口名{ 代码… }
-
特点:
- 接口中都是抽象方法
- 通过interface关键字创建接口
- 通过implements让子类来实现
- 可以理解成,接口是一个特殊的抽象类
- 接口突破了java的单继承的局限性
- 接口和类之间可以多实现,接口和接口之间可以多继承
- 接口是对外暴露的规则,是一套开发规范
- 接口提高了程序的功能扩展,降低了耦合性
-
接口里的“变量”必须初始化
-
接口里的“变量”都是静态的
-
接口里的“变量”都是常量
-
==实际上,接口里没有变量,只能有公开的静态的常量==,默认就含有
public static final
-
Java1.8以后,接口里可以存在普通方法,但接口里的普通方法必须使用默认的权限修饰符或是修饰成静态的static类
-
==接口中的静态普通方法必须通过类名静态访问,对象名方式无法调用==
-
接口里的方法,常量的声明都可以简写。接口会自动修饰完全
-
接口的复杂用法:
- 接口可以多继承
- 接口可以多实现
- 接口可以在单根继承的同时,多实现
-
==eclipse中一个类在实现接口时出现的bug:==
当准备好一个接口后(未保存—未编译),新建一个类实现这个接口,随后按照提示自动生成重写的方法,这时编译器报错!(The method xxxx of type xxxxx must override or implement a supertype method),此时的快速排错方案是:Remove '@Override' annotation。如果你点击快速排错删除‘@Override’后,还是会提示有错。对于这种现象其实就是因为实现的接口未保存,也就是未编译,你只需去到接口的界面Ctrl+S即可排除所有的错误。
-
类和类间的关系:extends
单继承关系,子类拥有父类的所有功能(除私有的和构造器)。
发生重写时,要求子类的方法声明与父类完全一致,并且权限不得小于父类的权限
-
接口和接口的关系:extends
既可以单继承也可以多继承,子接口可以使用父接口的所有功能
-
类和接口的关系:implements
既可以单实现也可以多实现
抽象类可以使用接口中的所有功能,但是实现类需要重写实现的所有接口中的所有抽象方法,否则就是一个抽象的实现类。
还可以在继承的时候多实现。
-
抽象类和接口的区别:
-
相同点:
都可以出现抽象方法
都不能被实例化
都需要子类重写抽象方法
接口和抽象类都体现了继承结构里的抽象层
-
不同点:
抽象类里有构造方法,接口没有构造方法
抽象类里有成员变量,接口里只能有公开的静态的常量
抽象类里有普通方法,接口里都是抽象方法(Java1.8后可以存在静态或者默认的普通方法)
接口里的成员可以简写,会自动补全
接口和接口之间是继承关系,而且可以发生多继承
实现类和接口之间是实现关系,而且是多实现
接口是为了突破Java单继承的局限性
-
-
抽象层的表现可以是接口也可以是抽象类,到底选择谁呢?
看需不需要提供方法体
-
如果都是抽象方法,那就设计成接口,还可以多继承多实现更灵活
-
如果有普通方法,那就不妨设计成抽象类,坏处是只能单继承
-
-
Overload和Override的区别:
- Overload是重载
- Override是重写
- ==重载体现的是编译时的多态,重写体现的是运行时的多态==
-
this和super的区别:
-
this代表本类对象的引用
-
super代表父类对象的引用
-
-
&和&&的区别:
-
|和||的区别:
-
a+=4和a=a+4的区别:
-
隐式转换和显式转换的区别:
单例模式指的是一个类,在一个JVM里,只有一个实例存在。
-
饿汉式单例模式:
package charactor; public class GiantDragon { //私有化构造方法使得该类无法在外部通过new 进行实例化 private GiantDragon(){ } //准备一个类属性,指向一个实例化对象。 因为是类属性,所以只有一个 private static GiantDragon instance = new GiantDragon(); //public static 方法,提供给调用者获取12行定义的对象 public static GiantDragon getInstance(){ return instance; } } package charactor; public class TestGiantDragon { public static void main(String[] args) { //通过new实例化会报错 // GiantDragon g = new GiantDragon(); //只能通过getInstance得到对象 GiantDragon g1 = GiantDragon.getInstance(); GiantDragon g2 = GiantDragon.getInstance(); GiantDragon g3 = GiantDragon.getInstance(); //都是同一个对象 System.out.println(g1==g2); System.out.println(g1==g3); } }
-
懒汉式单例模式:
package charactor; public class GiantDragon { //私有化构造方法使得该类无法在外部通过new 进行实例化 private GiantDragon(){ } //准备一个类属性,用于指向一个实例化对象,但是暂时指向null private static GiantDragon instance; //public static 方法,返回实例对象 public static GiantDragon getInstance(){ //第一次访问的时候,发现instance没有指向任何对象,这时实例化一个对象 if(null==instance){ instance = new GiantDragon(); } //返回 instance指向的对象 return instance; } } package charactor; public class TestGiantDragon { public static void main(String[] args) { //通过new实例化会报错 // GiantDragon g = new GiantDragon(); //只能通过getInstance得到对象 GiantDragon g1 = GiantDragon.getInstance(); GiantDragon g2 = GiantDragon.getInstance(); GiantDragon g3 = GiantDragon.getInstance(); //都是同一个对象 System.out.println(g1==g2); System.out.println(g1==g3); } }
-
饿汉式是立即加载的方式,无论是否会用到这个对象,都会加载。 如果在构造方法里写了性能消耗较大,占时较久的代码,比如建立与数据库的连接,那么就会在启动的时候感觉稍微有些卡顿。
懒汉式,是延迟加载的方式,只有使用的时候才会加载。 并且有线程安全的考量 使用懒汉式,在启动的时候,会感觉到比饿汉式略快,因为并没有做对象的实例化。 但是在第一次调用的时候,会进行实例化操作,感觉上就略慢。
看业务需求,如果业务上允许有比较充分的启动和初始化时间,就使用饿汉式,否则就使用懒汉式
-
单例模式三元素
- 构造方法私有化
- 静态属性指向实例
- public static的 getInstance方法,返回第二步的静态属性
API:Application Programming Interface,应用程序接口
-
String类是被final修饰的,它不可以被继承,字符串类型的变量不可以被修改。字符串的拼接其实是通过底层自动创建了一个可变的StringBuffer类型的可变字符串对象,通过这个对象来完成字符串的拼接,再toString成String类型。只要有字符串变量参与的拼接都会产生新的对象。
-
String类的工具类StringBuilder/StringBuffer。
==面试考点;StringBuilder与StringBuffer的异同:==
-
日期类Date
-
日期工具类SimpleDateFormat
-
商业计算工具类BigDecimal/BigInteger
==面试考点;BigDecimal与BigInteger的异同:==
-
==输入流(Input)进行读操作,输出流(Out)进行写操作。==
-
关于IO流的分类整理:
分类 字节输入流 字节输出流 字符输入流 字符输出流 ==抽象基类== InputStream OutputStream Reader Writer ==访问文件== FileInputStream FileOutputStream FileReader FileWriter 访问数组 ByteArrayInputStream ByteArrayOutputStream CharArrayReader CharArrayWriter 访问管道 PipedInputStream PipedOutputStream PipedReader PipedWriter 访问字符串 -- -- StringReader StringWriter ==缓冲流== BufferedInputStream BufferedOutputStream BufferedReader BufferedWriter 转换流 -- -- InputStreamReader OutputStreamWriter 对象流 ObjectInputStream ObjectOutputStream、 -- -- 对象流 FilterInputStream FilterOutputStream FilterReader FilterWriter 打印流 -- PrintStream -- PrintWriter 推回输入流 PushbackInputStream -- PushbackReader -- 特殊流 DataInputStream DataOutputStream -
字符流:只能操作字符文件.txt
-
==字节流:可以操作任何数据,应用更加广泛,应重点了解==
-
Java里的路径需要两根反斜杠
\\
因为一根反斜杠表示转义,两根反斜杠才能转义成一个反斜杠\
。 -
一般父类都会做程序设计,可能设计为抽象类或者接口,都不能实例化,我们只需要学习其共性方法
-
一般子类提供的方法都是继承自父类,没有单独研究的必要,只需要了解其构造方法,如何实例化
-
BufferedInputStream比FileInputStream快
-
普通流控制追加还是覆盖。高级流只提高效率,自定义追加还是覆盖模式需要调用普通流的可自定义模式构造器
-
关于字节流的常用构造方法及其方法:
-
FileInputStream
//实例化 InputStram in = new FileInputStream(new File("path")); //方法 int date = in.read();//返回值——int类型。一次只读一字节 //当想一直读取文件到结尾时 while((date=in.read)!=-1){ System.out.println(date); } //关闭流 in.close();
-
BufferedInputStream
//实例化 InputStram in = new BufferedInputStream(new FileInputStream(new File("path")));
-
FileOutputStream
//实例化 OutputStream out = new FileOutputStream(new File("path"),true/false);//最后的参数为选择追加还是覆盖模式 //如果没有最后的参数,默认为覆盖模式 //方法 out.writer(97);//输入int类型的参数 String s=""; byte[] b=s.getBytes(); out.writer(b);//输入参数为byte[] //关闭流 out.close();
-
BufferedOutputStream
//实例化 OutputStream out = new BufferedOutputStream(new FileOutputStream(new File("path"),true/false)); //模式选择由普通流负责,高级流只提高效率
-
-
List的遍历:
-
方式1.1:使用List继承来的Iterator迭代器遍历。我们通过while循环,通过调用其hasNaxt方法使其从开始遍历到最后。通过next方法返回遍历的数据
//方式1 Iterator<Integer> iterator=list.iterator(); while (iterator.hasNext()){ System.out.println(iterator.next()); }
-
方式1.2:使用List自己的迭代器ListIterator来进行遍历。正向遍历与使用父类的Iterator方法一样
//方式2 ListIterator<Integer> listIterator=list.listIterator(); while (listIterator.hasNext()){ System.out.println(listIterator.next()); }
-
方式1.3:当使用自己的迭代器Listiterator从前向后遍历完成后,可使用hasPrevious方法和previous方法从后向前遍历
//逆向遍历——必须先顺序遍历后才能进行逆向遍历 while (listIterator.hasPrevious()){ System.out.println(listIterator.previous()); }
-
方式2:单纯for循环。注意在这里List的长度是用size方法获取
for (int i = 0; i <list.size() ; i++) { System.out.println(list.get(i)); }
-
方式3:foreach(增强for)
for (Integer i:list) { System.out.println(i); }
-
-
Set的遍历
-
方式1:迭代器遍历
Iterator it=set.iterator(); while (it.hasNext()){ System.out.println(it.next()); }
-
方式2:foreach(增强for)
for (Integer i:set) { System.out.println(i); }
-
方式3:
for (Object obj: sets) { if(obj instanceof Integer){ int aa= (Integer)obj; }else if(obj instanceof String){ String aa = (String)obj } …….. }
-
-
Map的遍历
-
方式1:先取出Map的keySet,再使用迭代器遍历set并使用get方法通过键获得值
Set<T> set=map.keySet(); Iterator it=set.iterator(); while(it.hasNext()){ System.out.println(map.get(it.next())); }
-
方式二:先取出Map的keySet,再使用foreach循环遍历set并使用get方法通过键获得值
Set<T> set=map.keySet(); Iterator it=set.iterator(); for(T i:it){ System.out.println(map.get(i)); }
-
方式三:使用Collection集合接收由Map的values方法返回的集合,遍历这个集合即可得到Map的值
Collection<T> valueList=map.values(); for(T o:valueList){ System.out.println(o); }
-
方式四:使用Set<Map.Entry<Integer,String>>接收由map.entrySet方法返回的Entry类型的Set集合,其中Entry类型是封装好的单独的键值对
Set<Map.Entry<T,V>> set=map.entrySet(); for(Map.Entry<T,V> entry:set ){ System.out.println(entry.getKey()+":"+entry.getValue); }
-
方法五:使用Set<Map.Entry<Integer,String>>接收由map.entrySet方法返回的Entry类型的Set集合后直接使用迭代器遍历set中的元素
Set<Map.Entry<T,V>> set=map.entrySet(); Iterator it=set.iterator(); while(it.hasNext()){ System.out.println(it.next()); }
-
-
泛型:
-
泛型用在方法上,需要体现在两个位置:返回值前、参数列表中
public <E> void print(E e) {}
-
泛型用在接口/类上,需要在类名后添加泛型约束
public interface Collection<E> {}
-
泛型常用名称
- E——Element(在集合中使用)
- T——Type(Java类)
- K——Key(键)
- V——Value(值)
- N——Number(数值类型)
- ?——表示不确定的类型
-
泛型< >,其中尖括号里必须是引用类型,不能是基本类型
-
-
ArraryList和LinkedList
- ArraryList底层使用的是Object数组,LinkedList底层用的是双向循环链表
- ArraryList适用于访问,LinkedList适用于增删
- 二者都是线程不安全的
-
HashSet和TreeSet以及LinkedHashSet
- 详见下图
-
Map
- map是无序的
- map是键值对key-value类型
- key重复的话,value会被覆盖
-
HashMap和HashTable以及ConcurrentHashMap
- 详见下图
- ![数据结构1](E:\Markdown\Java 第一阶段笔记.assets\数据结构1.png)
- ![数据结构2](E:\Markdown\Java 第一阶段笔记.assets\数据结构2.png)
- ![数据结构3](E:\Markdown\Java 第一阶段笔记.assets\数据结构3.png)
-
进程:是程序的一次运行活动,是系统资源分配和调度的一个独立单位,有独立的地址空间和系统资源。打开一个浏览器、一个聊天窗口分别是一个进程。进程可以有多个子任务,如聊天工具接收消息、发送消息,这些子任务是线程。==资源分配给进程,线程共享进程资源==。
-
线程:是CPU调度和分配的基本单位。一个进程可由多个线程的执行单元组成,每个线程都运行在同一进程的上下文中,共享同样的代码和全局数据。每个进程至少有一个主执行线程,它无需由用户主动创建,一般由系统自动创建。系统创建好进程后,实际上就启动了执行该进程的执行主线程,执行主线程以函数地址形式,即程序入口函数(如 main函数),将程序的启动点提供给操作系统。主执行线程终止了,进程也就随之终止。
对比 进程 线程 定义 进程是程序运行的一个实体的运行过程,是系统进行资源分配和调配的一个独立单位 线程是进程运行和执行的最小调度单位 系统开销 创建撤销切换开销大,资源要重新分配和收回 仅保存少量寄存器的内容,开销小,在进程的地址空间执行代码 拥有资产 资源拥有的基本单位 基本上不占资源,仅有不可少的资源(程序计数器,一组寄存器和栈) 调度 资源分配的基本单位 独立调度分配的单位 安全性 进程间相互独立,互不影响 线程共享一个进程下面的资源,可以互相通信和影响 地址空间 系统赋予的独立的内存地址空间 由相关堆栈寄存器和和线程控制表TCB组成,寄存器可被用来存储线程内的局部变量 -
并发:并发是把CPU运行时间划分成若干个时间段,每个时间段再分配给各个线程执行,当一个线程在运行时,其它线程处于挂起状。从宏观角度是同时进行的,但从微观角度并不是同时进行。
![image-20200821142912017](E:\Markdown\Java 第一阶段笔记.assets\image-20200821142912017.png)
-
并行:并行是同一时刻当一个CPU执行一个线程时,另一个CPU可以执行另一个线程,两个线程互不抢占CPU资源,是真正意义上的不同线程在同一时刻同时执行。
![image-20200821142937872](E:\Markdown\Java 第一阶段笔记.assets\image-20200821142937872.png)
-
同步:
-
异步:
-
线程的五个状态:新建、就绪、运行、阻塞、死亡
-
实现多线程方式1——继承Thread类
public class Test1 { public static void main(String[] args) { MyThread myThread=new MyThread();//就绪状态 myThread.setName("--线程1--"); myThread.start();//可运行状态 MyThread myThread1=new MyThread(); myThread1.setName("--线程2--"); myThread1.start(); } } class MyThread extends Thread{ @Override public void run() {//运行状态 super.run(); for (int i = 0; i < 20; i++) { System.out.println(getName()); } } //死亡(结束)状态 }
-
实现多线程方式2——实现Runnable接口
public class Test2_Runnable { public static void main(String[] args) { MyRunnable myRunnable=new MyRunnable(); Thread thread=new Thread(myRunnable); thread.start(); Thread thread1=new Thread(myRunnable); thread1.start(); } } class MyRunnable implements Runnable{ @Override public void run() { for (int i = 0; i <20 ; i++) { System.out.println(Thread.currentThread().getName()+"=="+i); } } }
-
售票案例:
需求:设计4个售票窗口,需要售票100张
package cn.tedu.thread; /** * 测试 多线程售票 * @author sharetown * @date 2020/8/21 17:28 */ public class Test_Tickets { public static void main(String[] args) { //1、创建线程对象 MyTickets myTickets1=new MyTickets(); //2、启动买票线程 myTickets1.start(); MyTickets myTickets2=new MyTickets(); myTickets2.start(); MyTickets myTickets3=new MyTickets(); myTickets3.start(); MyTickets myTickets4=new MyTickets(); myTickets4.start(); } } //1、继承Thread类实现多线程编程 class MyTickets extends Thread{ //需求:设计4个售票窗口,总计售票100张 //定义变量,记录票数 int tickets=100; //2、开始买票业务 @Override public void run() { super.run(); while (tickets>0){ System.out.println(getName()+"----"+tickets--); } } }
-
以上代码虽然开辟了四个线程,但每个线程都是独立工作的,每个窗口都卖了一百张。
-
解决方案第一阶段(不完善):将资源改为静态资源,即加static关键字修饰
static int tickets=100;
-
解决方案第二阶段:加锁
-
同步锁—synchronized
-
概述
-
用法:通过synchronized关键字实现同步锁
==用在方法上:synchronized 权限修饰符 返回值 方法名([参数列表]) {}==
==用在代码块上:synchronized(锁对象){ 需要同步的代码 }==
用在方法上,使用锁对象默认就是this。用在代码块上时,锁对象可以任意
-
-
同步和异步的区别
- 同步:是指。同一时刻,有钥匙的是独占资源的,共享资源没有人抢,没有钥匙的人只能等待
- 异步:是指,同一时刻,没人排队,大家抢着去用共享资源
- 效率上来讲:异步>同步
- 安全上来讲:同步>异步
- 同步可以提高安全性,但是牺牲了效率/性能。异步提高了效率,但是牺牲了安全
-
多线程里的共享资源是被抢占的,是一个异步的,大家都去抢没人排队等待
解决方案:加锁,这样保证了数据的安全,因为同一时刻的共享资源是被独占的,但是降低了效率。加锁实现了一个同步的效果,就是有钥匙的排队等待,没钥匙的排队等待,虽然效率低,但是安全。
-
怎么判断程序中,数据有没有多线程编程时的安全隐患
==如果有多条语句操作了共享资源,一定会有多线程的安全隐患,必须加锁==
-
加锁在代码块上时,锁对象的选择形式:
-
synchronized(new Object()){}——锁住了不同的对象
-
Object obj = new Object();
synchronized(obj){}——锁住了同一个对象
或者
synchronized(this){}——锁住了同一个对象
-
synchronized(类名.class){}——当数据是静态的时。锁了本类,仅针对静态
-
-
拓展:读写锁
-
自定义注解
@interface MyAnnotation{ String local() default "class"; String value() default "";//特殊属性value }
-
为自定义注解添加位置和生命周期约束
//@Target(ElementType.METHOD)//描述注解可以出现的位置 @Target({ElementType.TYPE,ElementType.FIELD,ElementType.METHOD})//描述注解可以出现的位置——多个位置上 @Retention(RetentionPolicy.SOURCE)//描述注解的生命周期
-
自定义注解的使用
//2、使用注解:@MyAnnotation @MyAnnotation class Hello{ @MyAnnotation(local = "field") String name; @MyAnnotation(local = "method") public void show(){ } }
-
作用:
-
拿到.class文件里的数据进行解析
-
获取文件里的所有数据(公开的、私有的、构造、属性、方法)
-
所有数据都封装在
Class
工具类身上
-
-
获取Class对象
- Class.forName("类的全路径");
- 类名.class;
- 对象.getClass();
-
普通反射的应用
-
获取Class对象
public static void method() throws ClassNotFoundException { Class aClass1 = Class.forName("cn.tedu.reflection.Student"); Class aClass2 = Student.class; Class aClass3 = new Student().getClass(); }
-
获取构造方法
public static void method2() throws NoSuchMethodException { Class aClass = Student.class; //调用方法 Constructor[] cs = aClass.getConstructors();//获取所有公开的构造器 Constructor c = aClass.getConstructor();//获取一个构造器,默认无参构造 Constructor[] ccc=aClass.getDeclaredConstructors();//获取所有构造器(包括私有的) for (Constructor cc : ccc) { //获取构造方法的名字 System.out.println(c.getName()); //获取构造方法的参数 Class[] ct = cc.getParameterTypes();//获取方法的参数列表 System.out.println(Arrays.toString(ct)); } }
-
获取成员方法
public static void method3() { Class clazz=Student.class; Method[] methods=clazz.getMethods();//获取公开的所有成员方法们(包括自己的和父类的公开的方法) Method[] me=clazz.getDeclaredMethods();//获取自己的所有成员方法(包括私有的) for (Method method :me) { System.out.println(method.getName());//获取方法名 Class[] cc=method.getParameterTypes();//获取每个方法的参数列表 System.out.println(Arrays.toString(cc)); } }
-
获取成员变量
public static void method4() { Class clazz=Student.class; Field[] fields=clazz.getFields();//获取公开的成员变量(不公开无法获取) Field[] fields1=clazz.getDeclaredFields();//获取全部的成员变量,包括私有的= for (Field f:fields1) { System.out.println(f.getName());//获取变量名 System.out.println(f.getGenericType());//获取数据类型 System.out.println(f.getType());//获取数据类型 } }
-
通过反射技术创建对象
public static void method5() throws Exception { Class<Student> clazz=Student.class; Student stu=clazz.newInstance();//通过这种方式实例化其实也要出发构造器,而且触发的是无参构造 System.out.println(stu); //如何触发含参构造 //getConstructor(String.class)匹配构造方法里参数的类型的class对象 Constructor<Student> c1=clazz.getConstructor(String.class); Constructor<Student> c2=clazz.getConstructor(String.class,int.class); Constructor<Student> c=clazz.getConstructor(String.class,int.class,double.class); Student jack=c.newInstance("jack",22,149.0); Student Rose=c1.newInstance("Rose"); Student Jnmus=c2.newInstance("Jnmus",22); System.out.println(jack); System.out.println(Rose); System.out.println(Jnmus); }
-
不仅能获取类的public资源,也可以获取类里的private资源
-
使用反射调用方法
暴力反射除了正确的使用相应的API以外,还需要开启访问权限
public static void method3() throws Exception { Class<Person> clszz=Person.class; Method e = clszz.getDeclaredMethod("eat"); //暴力反射:除了使用正确的API,另外还需要开启访问权限 e.setAccessible(true); Person p=clszz.newInstance(); e.invoke(p); }
-
使用反射操作类的成员变量
public static void method2() throws Exception { Class<Person> p=Person.class; Field[] fie=p.getDeclaredFields(); Field ff=p.getDeclaredField("name");//拿到名为name的成员变量的对象 ff.setAccessible(true);//打开权限 Person obj=p.newInstance(); ff.set(obj,"jack");//给成员变量赋值 System.out.println(ff.get(obj));//获取成员变量的值 for (Field f:fie) { System.out.println(f.getName()); System.out.println(f.getType().getName()); } }
准备一个外部类和内部类
class Outer{//外部类
String name="jack";
public void out(){
System.out.println("out()");
}
class Inner{//内部类
int age;
public void in(){
System.out.println("in()");
}
}
}
特点:
-
内部类可以直接使用外部类的成员
-
外部类想访问内部类的成员,必须创建内部类的对象
-
在其他类中调用一个内部类中资源的方法:
外部类的对象.内部类的对象.想要调用的内部类资源
-
定义内部类的对象的方式为:
外部类名.内部类名 变量名
。其实外部类名.内部类名
就是变量的类型Outer.Inner outIn=new Outer().new Inner();//实例化内部类对象
位置在成员位置(类里方法外)的内部类叫成员内部类
成员内部类可以被private、static修饰
位置在局部位置(方法里)的内部类叫局部内部类
通常存在局部位置(方法里),通常要配合匿名对象使用
-
语法:(参数列表)->{方法体}
-
要求:接口里只能有一个抽象方法
-
与匿名内部类比较
public class TestLambda { public static void main(String[] args) { //匿名内部类 Inter in=new Inter() { @Override public void save() { System.out.println("数据保存成功"); } }; in.save(); //Lambda表达式——要求:接口里只能有一个抽象方法 //语法:(参数列表)->{方法体} Inter inter=()->{ System.out.println("Lambda表达式"); }; inter.save(); } } interface Inter{ void save(); }
-
当接口的抽象方法有参数时
interface Inter2{ void save(int id); } public static void main(String[] args) { Inter2 inter2=(int a)->{//括号里需要保持与抽象方法的参数列表类型一致 System.out.println(a); }; inter2.save(10);//调用时传参 }
-
有参数有返回值时
interface Inter3{ int save(String name); } public static void main(String[] args) { Inter3 inter3=(String name)->{ return 100;//有返回值就得按要求返回 }; System.out.println(inter3.save("das")); }
字符串与正则表达式进行匹配的方法,由String类提供提供一个返回值为boolean类型的实例方法——matches()。
public static void main(String[] args) {
System.out.println("请输入手机号码:");
String number=new Scanner(System.in).nextLine();
//判断格式是否正确
String reget="[1][0-9]{10}";
System.out.println(number.matches(reget));
}
![image-20200829114539043](E:\Markdown\Java 第一阶段笔记.assets\image-20200829114539043.png)