JavaSE语法笔记
本篇文章是作者自己的学习笔记,每一章没有详细介绍
Java 编程基础知识点整理
目录
Java 编程基础知识点整理
一、基本概念
1. 命名规则:
二、数据类型
2. 浮点型:
三、数据类型转化
四、运算符
五、控制台输入
六、方法
3. 调用:类名。方法名。
七、数组
八、面向对象和面向过程
九、类和对象
1. 构造方法名字与类名相同,不需要加 void 修饰。
十、static 关键字
十一、包
1. Java 中一个类的完整名称:包名(地址)+ 类名。
十二、访问权限修饰符
十三、面向对象的三大特点
4. 方法的重写:
5. 抽象类和接口的区别
十四、常用类(api)
2. BigDecimalDemo类:解决浮点数精度问题。
十五、集合框架
3. Set 接口:不能存储重复元素。
11. HashMap:
十六、泛型
十七、IO(输入,输出)/java.io
1. 按照读取单位来分:
2. 按照流的流向分:
3. 示例:
1. 字符流(只能读取文本文件,不能读取图片,视频):
2. 转换流:
2. 数据流(DataStream)。
十八、异常
1. 概述:在计算机语言进行开发过程中,即使代码很完善,但是很多
1. 常见的异常:
3. 异常处理通过五个关键字来实现:
7. 方法:
十九、网络编程
1. 三要素:ip 地址,端口号(对应的程序),通信协议。
2. 网络通信模型:
7. UDP 协议下的实现类:
二十、线程
1. 基本概念:
2. 线程和进程之间的关系:
3. 实现多线程的两个方法:
一、基本概念
- 关键字:有特殊含义的单词,在编写代码中不能作为变量名使用。
- 保留字:不是关键字,但也不能作为变量名使用,例如 goto、const 等。
- 标识符:在编写程序中,程序员能够命名的内容,为变量、函数、类、包等命名的字符序列称为标识符。
- 变量:在 Java 中变量的使用前必须是经过赋值的,没赋值不可以使用。变量是程序中最基础的存储单元,用来存储数据,变量就是在内存中开辟一块空间,通过变量名来访问空间。
二、数据类型
-
基本数据类型(8 种)
- 整形变量:byte、short、int、long。
- byte:1 个字节,8 个二进制位,取值范围 -128~127。
- short:2 个字节。
- int:4 个字节。
- long:8 个字节,在对 long 类型的变量初始化的时候,整形的字面常量默认为 int 类型,要是赋值超过 int 类型会报错,需要在末尾加个 L/l 告诉编译器赋值的是 long 类型,例如 long a = 12249823947239473298L。
- 浮点型:
- float:4 个字节,有效位数是 8 位,浮点类型默认字面常量是 double 类型,需要 float 类型时需要在字面常量后面加 f/F,例如 float a = 10.2f。
- double:8 个字节。
- 有的小数在底层的存储不能做到准确存储,十进制浮点数不能准确的化为二进制数,例如 0.3 在存储过程中转化为二进制后就变成了 0.30000000000001。
- 布尔型(逻辑)类型:boolean,值只能用 true/false 赋值,Java 中的逻辑表示为 true/false,不能用 0 或者非 0 表示,逻辑运算后的值也只能是 true/false。
- char 类型:2 个字节,只能表示一个字符,一个字符用 '',字符串用 "" 与 C 一致。计算机无法直接存储字符,将字符转化成对应的十进制编码,再以二进制存储在计算机中,Java 用的 Unicode 编码表,其中包含了 ASCII 编码,Java 底层采用 Unicode 编码存储数据,char 类型可以进行计算操作,可以看成整数。
- 整形变量:byte、short、int、long。
- 引用数据类型:牵扯到面向对象,Java 中有一个 String 的类,来表示字符串(先可以看成数据类型)。
三、数据类型转化
-
默认转化:小容量 -----> 大容量,例如 byte a = 10; int b = a; 不会报错直接转化,不用像 C 语言需要强制转化。
- 强制转化:大容量 -----> 小容量,强制转化与 C 一致,long a = 10; int b = (int)a; 但字面常量不能超过最大容量,使用的时候注意可能出现的问题:
- 溢出,因为数据类型本身的字节大小。
- 精度降低(浮点转为整数丧失了小数部分)。
- 大小关系:byte、char、short -> int -> long -> float -> double,long/int 小于 float,浮点数在内存中不是简单表示大小按照特定要求分配的内存。
- 不同的数据类型相加最终的结果是容量最大的数据类型,输出时与字符串相加会变成字符串类型,运算符优先级不变。
四、运算符
- 计算运算符:计算时会隐式类型转换,例如 short a = 10; a = a + 10; 报错,a += 10; 等价于 a = (short)(a + 10);。
- 逻辑与 / 逻辑或:&&、&、||、|,区别:&、| 不会出现短路现象,&&(也叫短路与)、||(也叫短路或)会出现短路现象,短路现象:如果逻辑值已经确定,右边的表达式不执行,例如 false && a++ > b,a 没有加一。
- 逻辑异或:^,逻辑值相同返回 false,逻辑值不同返回 true。
- 三目运算符:条件运算符,结果 = (表达式)? 结果一:结果二;如果表达式 == true 返回结果一,否则返回结果二,接收结果的变量必须和返回值数据类型一致,一定有返回值。
- 位运算符:是对整数的二进制位进行运算,正数移动后,符号位补 0,负数移动后,符号位补 1(>>,<<)。
- >>:二进制比特位向左移动,后位空缺补 0。
- <<:二进制比特位向右移动,前位空缺补 0。
- >>>:左移,无论正负数,符号位都补 0。
- &、|:当左右两边是整数时,是位运算,& 只有 1&1 时才是 1,| 只有 0|0 才是 0,^(异或) 相同为 0,不同为 1,计算机存储时,都用的是补码存储的。
五、控制台输入
定义:Scanner scanner(自主命名) = new Scanner(System.in);// 创建一个实例,使用:变量 = scanner.next ();
六、方法
- 定义:与其他语言中的函数类似,方法是解决一类问题的组合,表示一种功能和行为,将完成某个功能的一段代码封装到有名称的代码块当中,方法不能独立存在要定义在类里面。
- 定义格式:[访问权限修饰符][修饰符]返回值类型 方法名(参数){ [return 方法值]; }。
- 调用:类名。方法名。
-
方法重载:方法名相同,参数不同(顺序、个数、类型)的多个方法,称为方法的重载,方法重载跟方法的返回值类型没有任何关系。
七、数组
- 是一个容器,可以存储一组相同数据类型的数据(是一种引用数据类型,是一个对象),数组中既可以存储基本数据类型,也可以存储引用数组类型。
- 数组在创建时必须给定一个长度,在创建后长度不可改变。
- 声明和创建:
- 声明:int [] 数组名; 或 int 数组名[];,例如 int a[] = new int[5];// 在面向对象的语言中都需要创建一个实例,可以看成 C 中的 malloc,在内存中创建了 5 个 int 空间的区域。
- 创建 int 数组后会自动赋默认值 0;创建 String(引用数据类型)数组后会自动赋默认值 null;创建浮点数类型数组后会默认赋值 0.0。
- 遍历(增强版 for 循环):例如 for(int 变量名:数组名){ System.out.print(tmp); },变量就是用来接收从数组中取出来的元素,数组名就是需要遍历的数组,转义字符 \t 解决格式问题。
八、面向对象和面向过程
- 面向过程:核心是将实现功能的过程也就是实现的步骤,封装为函数,在每一步执行时,调用对应的函数即可,以函数为单位进行代码组织的。
- 面向对象:是一种编程风格,是一种程序设计的思想,解决问题是以类为单位进行组织的,将同一类事物的属性和行为封装到一个类中,面向对象可以解决一类问题,找对应的类,类与类之间还可以存在继承关系,使得代码的扩展性更好,维护性更好。
- 面向对象是一种宏观的设计思想分析出完成这件事情需要哪些类,面向过程是一种微观具体的设计思想,是具体落地实现,程序设计时,两种思想都要用到。
九、类和对象
- 在 Java 中要使用一个类必须创建实例,因为如果每次使用时不创建,那么类和静态变量无区别,定义了一个类相当于定义了一个复杂的数据类型和多个方法的集合。
- 类中包含变量与方法,类是一类事物的行为(方法)和特征(变量)的集合;对象是类中具体的一个个体,是类中的实例,面向对象注重交互性,面向对象和面向过程是一种思维。
- 类是参考现实生活中的事物,将现实生活中的事物的特征和行为提取出来,是抽象的是概念上的定义;对象是实际存在的,是类中的个体,对象是以类为模版,在内存中创建的一个实例。
- 每 new 出来一个对象,都是独立存在的,互不干扰,类中定义成员变量(全局变量)可以不赋值,系统可以自动赋默认值。
- 构造方法:new + 类名(); 为刚刚创建的类的成员变量进行初始化默认赋值,特点:
- 创建一个对象:类名 变量名 = new 类名();,这里将变量名称为引用类型变量,通过引用类型变量来访问对象,引用类型变量的值实际是就是对象在内存中的首地址;用 new 创建的空间都在堆区。
- 方法的重载:方法名相同,参数不同(顺序、个数、类型);方法的重载与方法的返回值无关。
- 对象与引用:保存了对象在内存中首地址的变量叫引用数据类型,= 接收的是对象的首地址,基本数据类型传入方法中到方法结束不能改变值,它仅仅只是值传递,引用数据类型传入方法中到方法结束可以改变值,因为它形参是地址。
- this 关键字:this 关键字表示当前当前操作(调用方法)的对象,使用 this 关键字 明确告诉程序 调用的是当前对象的成员变量,这样我们就可以在方法中区分成员变量和局部变量(因为重名),使用无参的构造方法时,可以通过 this 关键字调用有参的构造方法。
十、static 关键字
- static 被称为静态,可以用来修饰类的成员变量,成员方法,代码块,内部类。
- static 修饰的成员在内存中只有一份,与类一样,所有的成员共享一份,具体的值可以被修改。
- 只要类被加载了,静态成员就可以使用了,可不创建对象,一般建议使用类名调用(用实例名。也可以调用,但不推荐),静态成员不依赖于类的实例,被所有的实例共享。
- 静态的方法中只能用静态的变量不能用非静态的变量,因为用不是静态的变量需要先创建实例,非静态变量与类同时被加载出来,静态成员变量属于类,非静态成员变量属于对象。
- 在 Static 方法内部只能访问 Static 成员,像工具类方法,每次使用都是使用参数,不使用成员变量,这种就比较适合用静态,静态变量或者方法在使用时可以直接用类名。来调用,scanner 中的方法不是静态的是因为方法中使用了非静态的成员变量。
- 静态代码块在类被调用时会自动执行,只执行一次,非静态代码块在创建对象时会自动执行,只执行一次(创建对象时类如果没有加载过,那么类也会被加载)。
十一、包
十二、访问权限修饰符
- public:公共权限,可以在项目中任何位置都可以访问到,可以修饰,类,成员变量,成员方法,内部类。
- protected:受保护权限,可以在本类,同包的其他类,不同包的子类可以访问到,可以修饰,成员变量,成员方法,内部类。
- default(空,什么都不写):默认权限,可以在本类,同包的其他类中访问到,可以修饰,类,成员变量,成员方法,内部类。
- private:私有权限,只能在本类中访问到,可以修饰成员变量,成员方法,内部类。
十三、面向对象的三大特点
-
封装:
- 在编程中的两种含义:
- 封装 - 包装,例如在一段重复使用的代码包装到一个方法中,称为包装。
- 面向对象语言的特性,使用不同的访问权限修饰符,控制类中的成员是否可以对外可见。
- 面向对象封装:将类中的某些信息隐藏到类的内部,不让外界直接访问,在对被封装的属性我们必须提供一个 set 和 get 来给它赋值和获取它。
- 好处:
- 隐藏类的信息。
- 方便加入控制语句。
- 通过特定的方法访问。
- 方便修改实现。
- 在编程中的两种含义:
-
继承:
- 定义:多个类中存在相同属性和行为时,将这些内容抽取到单独一个类中,那么,多个类中无需再定义这些属性和行为,只需要和抽取出来的类构成继承关系。
- 好处:
- 继承的出现减少了代码的重复,提高了代码的复用性。
- 有利于功能的扩展。
- 让类与类之间出现了 is-a 的关系,为多态的使用提供了前提。
- 使用:
- 把同类中公共的一些属性和行为抽取到一个父类中。
- 让子类继承父类中非私有的属性和行为,子类拥有父类的成员变量和成员方法但不能直接访问父类私有的成员变量和方法。
- 在 Java 中一个类只能拥有一个父类,不能同时继承多个类,但可以多层继承,父类的父类,一个父类可以拥有多个子类。
- 在 Java 语言中,它提供了一个特别的类,java.lang.object,所有的类间接或直接的继承它,当一个类没有继承其他类时,会默认继承 object 类,所以一个类中会存在没有定义在本类中但是可以使用的方法。
- 方法的重写:
- 当子类与父类的方法实现不同时,可以在子类中对父类的方法进行重写,父类中的方法不能满足子类的需求时,可以在子类内部进行方法的重写,子类中的方法会覆盖父类中的方法但不会改变父类中的方法。
- 方法的重写不能对构造方法,静态方法进行,方法的重写必须与父类方法的方法名,参数列表,返回值相同,子类重写的方法权限不能小于父类的方法权限,重写的方法需要与父类结构保持一致,访问权限不一样,大于等于父类权限。
- 注意:父类的私有方法不能重写,跨包的父类默认权限不能重写,成员变量不存在重写,@Override Java 中帮助检测方法的重写,检查结构是否与父类一致,叫做注解,添加在重写的方法上,例如 @Override public void eat(){}。
- super 关键字:
1.在 Java 中可以用 super 关键字在子类中调用父类中的指定操作,可以用 super 调用父类中的成员变量,成员方法,super 可以在子类的构造方法中调用父类的构造方法。
2.使用 super 可以更加清楚的更加清楚的分辨子类属性与父类属性,super不仅可以追溯父类还可以追溯父类的父类。
super和this用法相似,this表示的是当前对象,super表示的是父类的内存空间。
在创建子类的对象时,并不会创建父类只是将父类的信息在子类中加载,并没有创建。
子类继承父类时不会继承父类的构造方法,只能通过super(参数列表)调用父类构造方法,必须声明在构造方法的首行,不写出来也是默认存在的。
放在第一行调用父类中的构造方法是因为先要对父类中的成员进行初始化。
如果子类构造器中既未显式调用父类或本类的构造器,且父类中又没有空参的构造器,则编译出错。也就是说在父类中写了不是无参数的构造方法但是没有写空的构造方法,且没有在子类中用super调用在父类中创建的构造方法,则编译出错,因为在 Java 语言中不允许无初始化。
3. 抽象类(abstract)
父类中有些方法的实现与子类都不相同,子类都需要对方法重写,那么父类方法就有些多余。
可以把这些方法定义成抽象方法,只有声明没有方法体(内容),让子类继承重写。
当一个类中包含不完整的信息(抽象方法),那么这个类就是一个抽象的类。
一个类中有抽象方法那么这个类一定是抽象类,但是抽象类不一定有抽象方法。
抽象类是不能创建对象的,因为里面可能包含不完整的抽象方法。
抽象类虽然不能创建对象,但是可以通过子类来调用抽象类中的非抽象方法。
抽象类主要定义一些公共的功能让子类来继承重写,父类定义功能子类来写。
当一个类继承抽象类之后:
- 要么将抽象类中的抽象方法全部方法重写。
- 要么继续将这个类定义为抽象类。
如果某个类中包含抽象方法,那么这个类必须定义成抽象类。
- 多态:
父类引用指向子类对象,同一种事物在不同的时刻表现不同的状态(向上转型,数据类型转换,把子类类型向上转为父类类型)。
例如animal dog = new Dog();
编译时调用的是父类中的方法,运行时调用的是子类对象中的方法(编译看左边,运行看右边)。
理解 Java 中为什么要设计object这个所有类的父类:
所有的类都是object的子类。
可以object可以接收和表示任意的类,从而实现多态。 - final(最终)关键字
用于修饰方法,类,参数,属性(成员变量)。
被修饰的类:不能被继承,不能被定义为接口,不能被定义为抽象类,如果我们的类不想被继承就可以用final继承,例如String就是被final修饰的类。
被修饰的方法:在子类中不能重写。
被修饰的参数:不能在方法中修改,例如final int age;
被修饰的属性:在定义时赋值或者用构造方法赋值,在此之后不能被修改(常量)。- 方式一:例如public static final double pi = 3.1415926;,用static的原因是:因为常量在内存中只需要一份就可以。
- 方式二:在定义时并不知道常量的值是多少,写一个构造方法来给它赋值(不用static修饰)。
- 接口(interface)
接口和抽象类很相似都可以定义功能让其他类去实现;
程序设计时我们关系功能,不关心细节。
接口可以看成一种特殊的抽象类,包含抽象方法,比抽象类更加单纯(因为抽象类中有许多属性和非抽象方法)。
接口是一种抽象的是不能创建对象的。
在接口中public static final int tmp = int tmp;
3 和 4 是在 jdk1.8 之后更新的。
接口中可以定义四种内容:- 常量。
- 抽象方法(默认)pubic void eat() = abstract void eat();。
- 静态的方法(可以用类名直接调用)。
- 默认方法(让子类进行调用)public default void test();子类中可以直接调用也可以进行重写。
类继承接口不用extends用implements(实现)。
一个类实现接口要么重写接口中所有的抽象方法或者把类定义为抽象类。
接口不能实例化,因为接口中有未实现的抽象方法。
- 类与接口,接口与接口之间的关系
一个类只能继承一个类。
一个类默认继承object。
一个类可以实现多个接口。
接口也可以继承接口(支持多继承,一个接口继承多个接口)。
类与类考虑继承,类与接口考虑实现(implements),接口与接口考虑继承。
接口与类之间也存在多态性。 - 抽象类和接口的区别
- 相同:
- 都可以有抽象方法。
- 都不能实例化(因为有未实现的方法)。
- 不同:
- 抽象类可以包含成员变量,非抽象方法,构造方法。
- 接口只能包含常量,抽象方法,静态方法,默认方法。
接口可以看成简单的抽象方法。
- 相同:
十四、常用类(api)
- Object类
toString();返回一个对象的字符串表现形式,输出的是一个字符串,输出的是实例的地址,不符合我们的要求,所以在子类中进行重写。
输出一个对象时会默认调用toString输出。
equals(object obj);它比较的是两个对象的地址是否相等(==),不符合我们的需求,所以我们进行重写。
instanceof是 Java 的保留关键字,它的作用是测试它左边的对象是否是它右边的类的实例,返回boolean的数据类型,当对象是右边类或子类所创建对象时,返回true;否则,返回false。
Java 中几乎所有类都对object中的equals方法进行重写用来比较两个对象是否相等,用==用来比较两个对象地址是否相等。 - Arrays类(java.util)
字符串比较大小需要调用String类中的compareTo比较大小。- equals方法,用于判断两个数组的值是否相同。
- toString方法,将数组的值转化为字符串形式向控制台输出。
- sort方法,也就是冒泡排序,从两两交换的形式进行排序,具有稳定性,作用是将数组有序化,可以分段排序。
使用sort(冒泡)排序时,如果想要对自定义的对象进行排序需要提供一个规则,指定用那个属性进行排序(Comparable接口中,compareTo方法进行重写)。
此方法会在Arrays中的sort方法自动调用。 - copyOf方法,作用是将指定数组赋值给另一个指定长度的新数组,并返回给新数组。
- fill方法,将指定的值赋给指定的数组。
- binarySearch方法,二分查找,通过一次比较去除一半的元素,重复此操作,直到找到或者全部查找完,这个算法的优点是时间复杂度低,效率高,但前提是数组有序,可以分段查找。
- 基本数据类型包装类(java.lang)
基本类型不是面向对象的,使用起来就不方便,所以我们就为 8 种数据类型包装了类。
例如int ->Integer
Integer.toBinaryString(10)// 二进制。
Integer.toHexString(10)// 十六进制。
Integer.toOctalString(10)// 八进制。
实例方法对对象中的数据进行比较。
System.out.println(i1.equals(i2));// 比较两个integer对象中的内容是否相等。
System.out.println(i1.compareTo(i2));// 比较连个integer对象中的内容大小,-1、0、1。
String s = i1.toString();// 将整数转为string,有时候将整数转化为字符串处理更加方便。
int a = i1.intValue();// 将值取出来,转化为基本数据类型。
Integer i3 = new Integer(10);
int c = i3;// 自动拆箱,底层会默认调用intValue,在计算机领域把这种简化语法称为语法糖。
int a0 = 10;
Integer a = new Integer(a0);
Integer a1 = a0;// 自动装箱时,它默认会调用valueOf()的方法,Integer a1 = Integer.valueOf(a0);
Integer.parseInt();可以将String类型转为int类型。
自动装箱时需要注意:
Integer a1 = 100;
Integer a2 = 100;
Integer a3 = 1000;
Integer a4 = 1000;
sout(a1==a2);true,说明在同一地址。
sout(a3==a4);false,说明不在一个地址。
问题出在valueOf()方法。
源码:
收起
plaintext
public static Integer valueOf(int i) {
if (i >= IntegerCache.low && i <= IntegerCache.high)
return IntegerCache.cache[i + (-IntegerCache.low)];
return new Integer(i);
}
整数值在 - 128——127 之间返回的是Integer类中的数组,所以值相同,所得到的地址相同,在这个范围之内用equals方法比较内容。
不在这个范围之内每一次都是得到一个新的对象。
如何将字符串转为整型:
- Integer i1 = new Integer("字符串");,int tmp = i1.valueOf();。
- String str = "10",int a = Integer.parseInt(str);。
使用valueOf是将integer转化为String类型。
如何将任意类型转为字符串:
toString()是将引用类型转化为字符串。
String.valueOf(参数);这里的参数可以是任意数据类型,数组也可以。
- String类(java.lang)
String直接赋值,直接赋值如果值相同,那么地址相同。
先去字符串常量池中(方法区中)查找有没有值 "abc" 的字符串对象,如果没有则在常量池中创建一个值为”abc” 的对象,如果常量池中有,则直接指向该字符串对象地址即可,不用新创建对象。
new一个对象,new一个对象值相同,地址不相同,会在堆区中直接开辟内存。
String str = new String("abc");
new在堆区中开辟了一块空间,str是指向那片空间的对象。
但是 "abc" 被存放进了字符串常量池中,如果常量池中已经有了 "abc",那么上述式子只创建了str对象,如果没有那么上述式子创建了str对象和 "abc" 对象(存放在字符串常量池中)。
String类是被final修饰的,其他类不能继承。
String类中,有一个char数组用来存储字符串的内容。
字符串的对象一旦创建后就不能改变了(因为char数组是被final修饰的),一旦值改变了就会创建一个新的字符串对象。
建议字符串比较时用equals()方法做比较。
方法:- boolean equals// 比较两个字符串内容是否一致。
- boolean equalsIgnoreCase// 比较两个对象内容是否相等,忽略大小写。
- boolean contains// 判断是否包含一个指定的子串。
- boolean isEmpty// 判断字符串的值是否是空串,是空串返回true。
- boolean startsWith// 判断字符串是否以某个字符串开头。
- boolean endsWith// 判断字符串是否以某个字符串结尾。
- length// 获取字符串长度。
- charAt// 获取字符串中指定位置的字符。
- indexOf(String str)// 获取指定字符在字符串中首次出现的位置,从前向后。
- indexOf(String str int fromIndex)// 从指定位置开始查找,获取指定字符首次出现的位置。
- lastIndexOf(string str)// 从后向前找。
- lastIndexOf(string str,int fromIndex)// 上一个方法的重载。
- substring// 从指定位置开始截取到末尾,截取后返回的都是一个新的字符串对象。
- substring// 从指定位置开始截取到指定位置结束,不包含结束位置,截取后返回的都是一个新的字符串对象。
- valueOf// 用于把各种数据类型转化为字符串,在实际开发过程中toString用的较少,String.valueOf用的比较多,因为当数据为null时,toString会报错,但valueOf不会报错,会把null输出出来。
- toLowerCase// 将字符串中全部字母转为小写字母。
- toUpperCase// 将字符串中全部字母转为大写字母。
- concat// 用于连接两个字符串。
- trim// 用于消除字符串最前面和最后面的空格。
- split("分隔符")// 用指定的字符将字符串进行拆分成字符数组。
- replace// 用一个指定的字符串把字符串中已有的字符串替换。
- toCharArray// 将字符串转化为字符数组。
- getByte// 将字符串转化为byte数组。
- StringBuffer类
概述:如果我们使用String声明的字符进行拼接,每一次都会创建一个新的对象,StringBuffer类就可以很好的解决这个问题。
所以 Java 中又提供了两个值可变的字符串对象StringBuffer,StringBuilder。
append("abcd");// 向末尾位置添加元素。
insert(1,"xx");// 向指定位置插入元素。
deleteCharAt(1);// 删除指定位置的元素。
delete(0,5);// 删除指定区间的内容,不包含结束位置,实际上就删除了 0-4。
replace(0,5,"xxx");// 替换指定区间的元素,不包含结束位置。
reverse();// 逆序数组。
StringBuffer不能直接赋值,只能通过创建对象用构造方法赋值。
它的内部有一个没有被final修饰的数组,默认长度是 16,就可以向数组中存放字符,装满后,数据就会自动扩容。
不会创建新的StringBuffer对象,只会扩容内部的数组,提高了效率。
StringBuilder类和StringBuffer类功能完全一致,但是StringBuffer是多线程场景下安全的,而StringBuilder不适合多线程场景使用。
String:是字符常量,每次修改会创建新对象,适用于少量字符串。
StringBuffer:是可变字符串,适用于单线程场景下使用,多线程场景下是安全的,因为它的方法上都加连锁。
StringBuilder:是可变字符串,适用于多线程场景下使用,它的方法上都没有加锁。
Sting类底层数组被final修饰了,所以不可变。
StringBuilder和StringBuffer底层数组没有被final修饰,所以可变。 - Math类(java.long)
abs(绝对值)。
sqrt(平方根)。
pow(double a,double b)//a的b次方。
max(double a,double b)。
min(double a,double b)。
random()返回 0.0 到 1.0 的随机数。
long round(double a)将double类型转化为long类型 (四舍五入)。 - Random(java.util):此类用于产生随机数。
构造方法:public Random()。
成员方法:
public int nextInt();。
public int nextInt(int n);在 0 到指定数 (不包含) 范围内随机产生一个数。 - Date类:使用Date类代表当前系统时间,表示当前系统运行的那一刻时间。
getTime()获取的是自 1970.1.1 00:00 至date对象创建的毫秒值。
Date date2 = new Date(1739429644863L);// 有参构造方法,long类型(getTime获取的时间)。 - Calendar类:是一个抽象类,使用的时候实现特定子类对象。
Calendar calendar = Calendar.getInstance();// 获取Calendar的一个子类对象。
calendar.get(Calendar.YEAR)// 年。
calendar.get(Calendar.MONTH)+1// 月。
calendar.get(Calendar.DAY_OF_MONTH)// 日。
calendar.get(Calendar.DAY_OF_YEAR)// 今天是今年的第多少天。
calendar.getTimeInMillis();// 返回 1970 年 1.1 00:00 至今的毫秒值。
SimpleDateFormat日期格式化类
format方法用来将date转化为字符串类型。
parse方法用来将String类型转为date类型,可以用这个方法指定日期(Date类型)。
日期类型转为指定类型字符串,从后端向前端传递数据时的转换:
Date date = new Date();
String s;
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss S E");
String str = simpleDateFormat.format(date);//将日期转为字符串
System.out.println(str);
字符串类型转为日期类型,从前端向后端发送数据时,在后端进行转换:
String dateStr = "2002-01-02";
SimpleDateFormat simpleDateFormat1 = new SimpleDateFormat("yyyy-MM-dd");
Date date1 = simpleDateFormat1.parse(dateStr);
System.out.println(date1);
实际开发中,存储的数据长度是不定的,是会变化的。
在存储数据时,不同的使用场景,可以使用不同的数据结构。
十五、集合框架
- 单列集合 collection 接口:只能存储单个值。
- List 接口(有三个实现类):可以存储重复数。
- ArrayList(数组列表):有其他接口 / 类中没有的方法,方法更侧重于数据结构。
- LinkedList(链表列表)。
- Vector(数组列表,添加同步锁,线程安全)。
- List 接口集合迭代:获取数组长度 a.length,获取字符串长度 str.length(),集合长度 list.size()。
- for 循环遍历:支持遍历时删除数据,注意,数组删除后后面的元素前移 [a,a,a,b,c],中间的 a 就删不掉。
- 增强 for 循环遍历:不支持遍历时删除元素,如果删除数据程序会报错。
for(String t:arrayList){
sout(t);
} - 迭代器遍历(Iterator):集合中通过,专门用来遍历迭代器。支持遍历时删除数据,不会漏删数组数据。
if(item.equals("a"){
iterator.remove();//删除必须调用迭代器中的remove()方法
}
迭代器对象的使用:
Iterator<String> iterator = arraylist1.iterator();//默认从第0个开始
while(iterator.hasNext()){//判断下一个结点是否为空
String item = iterator.next();//指向下一个,并返回当前结点信息
System.out.println(item);
}
迭代器 2:
ListIterator<String> listIterator = arraylist1.listIterator(可以放指定位置参数);
while(listIterator.hasNext()){
String item = listIterator.next();
System.out.println(item);
}
- Set 接口:不能存储重复元素。
- HashSet 哈希(散列表):本质就是一个数组,算出余数是多少存在哈希表对应位置,存储的元素无序。用链表解决冲突,用数组结构存储(查询快)。默认数组长度为 16,哈希余数为 16。hashSet.remove(7);//根据内容删,因为有冲突,hashSet.iterator();//获得一个迭代器。
public HashSet() {
map = new HashMap<>();
}
hashSet 中创建了一个 hashMap,用于使用 hashMap 中的 key。
hashSet 是如何实现不能出现重复的(hashMap 的 key 不重复原理):
底层会用到两个方法:hashCode() 和 equals()。
先调用 hashCode() 方法,根据内容算出哈希值(int 值),比用 equals(用循环一个一个字符比较很慢)比较快,例如 String str = "aaaaaa...."。
但是用哈希值判断是有风险(内容不同,哈希值相同)(通话和重地)。
所以在哈希值相同的情况下,再调用 equals() 判断内容。
优点:既保证效率又保证安全。
public HashSet(Collection<? extends E> c) {
map = new HashMap<>(Math.max((int) (c.size()/.75f) + 1, 16));
addAll(c);
}
key 为 null 时,哈希值默认为 0。
HashSet 里只使用 hashMap 中的 key。
结论:向 HashSet 中添加数据类型(自己写的类)时,必须提供 HashCode 和 equals 方法。
向 HashSet 中存储我们自己定义的类的类型,如果没有重写 hashcode(),默认会调用 object 中的 hashcode 方法。
object 类中 public native int hashCode(); 是一个 native 修饰的方法(是本地方法,是由操作系统操作),读取的是对象的内存地址。
现在的需求是,对象中的内容相同,就判定为重复内容。
需要对 object 类中的 equals 和 hashcode 进行重写,根据内容计算哈希值,判断内容是否相等。
set 接口中遍历方式不能使用 for 循环,因为没有下标索引,只能使用增强 for 和迭代器。 - TreeSet(二叉排序树,左小右大):存储后会自动排序,TreeSet 底层基于树形结构实现,元素进行指定方式的排序,存储的类型必须实现 Comparable 接口。
- HashSet 哈希(散列表):本质就是一个数组,算出余数是多少存在哈希表对应位置,存储的元素无序。用链表解决冲突,用数组结构存储(查询快)。默认数组长度为 16,哈希余数为 16。hashSet.remove(7);//根据内容删,因为有冲突,hashSet.iterator();//获得一个迭代器。
- Map 接口:双列集合(一个结点可以存储两个数据),一个 key 映射唯一一个 value。
实现 Map 接口的实现类都是双列集合,可以存储一组键值对。
键不能重复,值可以重复,键重复的话,后来的会把前面的覆盖掉。
Map 实现类中的常用方法:- put(K key,V value)// 添加数据。
- remove(Object key)// 删除数据,传入 key 并返回 value。
- void clear()// 清除数据。
- boolean containsKey(Object key)// 判断是否包含 key。
- boolean containsValue(Object value)// 判断是否包含 value。
- boolean isEmpty()// 判断是否为空。
- int size()// 有多少个元素。
- get(Object key)// 查找元素并返回 value。
- Collection<V>// 返回一个只有 value 值的 connection 单列集合。
- Set<K> keySet()// 返回一个只有 key 值的 set 接口。
实现类: - HashMap:
- 增强 for:
Set<String> set = hashMap.keySet();//将双列集合转为单列集合
for (String key:set)
{
System.out.println(key+":"+hashMap.get(key));
} - 迭代器:
Iterator<String> iterator = hashMap.values().iterator();
while(iterator.hasNext())
{
System.out.println(iterator.next());
}
HashMap 可以存储一个为 null 的键(有且仅有一个),值都可以为 null(可以多个)。
底层用到的数据结构:哈希表(数组),链表,红黑树。
- 增强 for:
- TreeMap:基于红黑树结构的实现,可以通过 key 的自然顺序进行排序。如果需要得到一个有序的 Map 就应该用 treeMap,Key 值的类型必须实现 Comparable 接口。如果键重复,后面键的值会替代前面键的值。
- HashTable:也是哈希结构,方法都加了锁,在多线程场景下是安全的。不能存储为 null 的键和值。
Map 集合遍历: - 方式一:先拿到所有的 key,循环 key,每一次拿到一个 key,在上 map 中找值。
for(String str:keyset){
sout(map.get(str));
} - 方式二:把所有的键值对进行提取,存储到一个 entry 类型集合中,不需要用 key 提取 value,比第一种方法效率高。
TreeMap<String,String> treeMap = new TreeMap<>();
Set<Map.Entry<String, String>> entries = treeMap.entrySet();
for (Map.Entry<String,String> entry:entries)
{
System.out.println(entry.getKey()+":"+entry.getValue());
}
- connections 类:是 Connection 中的一个工具类,与数组的工具类 Arrays 类似。
swap(list,i,j);//把list中i索引和j索引交换元素。
copy(list1,list2) 把 list2 复制到 list1 中,但 list1 尺寸必须大于等于 list2。
fill(list,object) 将集合中所有值替换。
reverse()// 逆序操作。
shuffle()// 随机排序。
addAll()。
十六、泛型
- 泛型(参数化类型,指定参数只能用指定的类型,统一类型,将类型当做参数传递),常用的类型参数有 T(type)、E(element)、K(key)、V(value)。
- 泛型的类型参数只能是类类型,参数可以是多个,如果没有指定类型,默认为 Object。
- 没有为集合定义泛型类型,可以存储任意的类型。一个集合中存储多种数据类型,存储时没有任何问题,对后续的处理数据就比较麻烦。
- 泛型接口:当一个类继承或实现泛型类 / 接口时,子类和父类要么都定义为泛型,要么子类不是泛型类,必须明确父类的类型。
- class A<T> extends Demo<T>// 子类和父类都是泛型类。
- class A extends Demo<String>// 子类不是泛型类,父类是泛型类且要明确数据类型。
- public interface Demo<T>// 定义一个泛型接口。
- public class A implements Demo<String>// 子类不是泛型类,父类要明确泛型的数据类型。
实现接口 / 继承时要记得重写接口中的抽象方法。
十七、IO(输入,输出)/java.io
- File 类(文件):一个 file 类对象可以表示文件,目录(文件夹)。
- lastModified// 获取文件最后修改的时间。
- length// 文件内容长度(File 数组的 length 是数组长度,文件夹.length)。
- getName// 文件名。
- getPath// 路径。
- isHidden// 判断是否是隐藏文件。
- canRead// 判断文件是否只读。
- canWrite// 判断文件是否可写。
- isFile// 判断是否是文件还是文件夹,是文件返回 true。
- isDirectory// 判断 File 对象表示的是否是文件夹。
- exists// 是否存在。
- createNewFile// 创建一个文件。
- mkdir// 创建一个文件夹。
- mkdirs// 创建一个多级的文件夹 E:/demo/a/b/c/d。
- delete// 删除文件 / 文件夹必须为空才能删除。
- list// 返回给定目录所有的子级(有可能是文件夹,还有可能是文件)。
- listFiles// 返回给定目录所有的子级文件,用 file 数组接收。
- IO 流:在 Java 语言中,提供许多可以进行读和写操作的类,我们把这些类形象的称为流,是处理数据(输入 / 输出)的通道,IO 流 -> Java 提供读写文件的类。
收起
java
//创建一个输入的管道,对接到我们要读取的文件上FileInputStream fileInputStream = new FileInputStream("C:/1javaSE/ffyc.txt");//创建一个输出的管道FileOutputStream fileOutputStream = new FileOutputStream("C:/1javaSE/ffyc1.txt");//只能创建文件,需要给一个文件(有名字)int b= 0;while((b=fileInputStream.read())!=-1){//读到末尾会返回-1
fileOutputStream.write(b);//输出往外写,先将字符转为机器码,输出机器码,系统自动把机器码转为字符}
fileInputStream.close();
fileOutputStream.close();//断开连接的文件
-
- 先创建一个输入流的管道(对象),给定输入的文件地址(路径 / 创建一个 file 对象),文件必须存在,如果不存在,抛出异常。
- 创建一个输出流管道(对象),给定输出的文件地址(可以不存在,不需要我们自己创建,输出管道会自动创建)。
- 读写:
read();每次读取一个字节,返回一个 int 类型。
int a = 0;// 接受每次读到的字节,读到 -1 时,则说明读到结尾。
OutputStream.write(a);// 输出读到的字节。
close()// 关闭通道,不关闭的话文件会被一直占用,不能进行其他操作。
byte[] byte;// 存储每次读取的字节内容。
int i = inputStream.read(byte []);//读写效率高,每次读 byte 数组个字节内容,返回值也是 int,返回的是往数组中实际装入的字节个数,读完会返回-1;
outputStream.write(byte,0,i);每次往后写 byte 数组个字节内容,从 0 开始写,写 length 个。
-
- 字符流(只能读取文本文件,不能读取图片,视频):
- 输入流(抽象类 Reader):FileReader(实现类)// 一次读取一个字节。
- 输出流(抽象类 Writer):FileWriter(实现类)// 一次输出一个字节。
- 转换流:
- (InPutStreamReader):把字节转换为字符,读取字节,并将其解码为字符。
- (OutPutStreamWriter):把字符转为字节。
- 节点流:流对象直接对接数据源,如文件,字符串,字符串数组等。
- 处理流 / 包装流:流对象封装的是其他的节点流对象,可以对我们读取到的数据进行二次处理。
- 缓冲字节流:内部有一个 8192 字节的数组,进行一个缓存,如果节点流对象数组长度大于缓冲字节流对象数组长度,内容就直接往硬盘上写了。减少程序与操作系统的交互(效率变快),不直接使用字节流是因为(数组长度太大,占的内存太多)。节点流先向缓冲字节流存储数据,缓冲字节流存储满了再向硬盘输出。BufferedOUtPutStream(缓冲字节输出流),BufferedInPutStream(缓冲字节输入流),bufferedOutputStream.flush();// 刷新缓存区,防止缓存区上还有内容,但是程序已经结束。
- 数据流(DataStream)。
- 缓冲字符流(BufferedReader/BufferedWriter)。
- 对象流(ObjectStream):Java 中的对象都存储在内存中,一旦程序运行结束,内存中的对象都会消失,数据也就没有了。有时,需要在程序停止时,将对象的信息或者数据保存下来,下次启动程序时,继续使用。IO 中就提供了对象输入,输出流。这种对象操作还称为对象的序列化(输入)和反序列化(输出),反序列化也是 Java 中创建对象的方式之一(其实并不是之前那个对象,只是将它的信息存储到文件中)。要存储(序列化)我们自己的类时记得要实现 Serializable 接口,会为我们的类自动生成一个唯一编号,否则编译器会报错。自动生成的编号,一旦我们的类信息发生变化,编号会改变,一般情况下我们可以为类生成一个固定的编号(serialVersionUID),当类信息改变时编号不会发生变化。
- 字符流(只能读取文本文件,不能读取图片,视频):
十八、异常
继续
很多问题不是靠代码可以解决的(比如:输入格式,读取文件是否存在),程序运行过程(编译通过后)中出现的异常(语法错误是连编译都没有通过,不属于异常)。
2. 异常抛出机制:Java 中把不同的异常类型用不同的类表示(封装),一旦发生某种异常,就创建该异常类型的对象,并且抛出,然后程序员可以捕获这个异常,并处理。
- 什么都不管,程序运行到异常停止。
- 异常处理,接收抛出的对象,提供处理程序,让后面的程序可以继续执行。
- 常见的异常:
- 数组越界异常。
- 类型转换异常。
- 数字格式化异常(字符串中有字母无法转为 Integer 类型)。
- 空指针异常。
- 算术异常。
- Java 异常体系结构:Throwable(java.lang 中的类,所有异常的父类)可分为两类:Error 和 Exception。
- Error:Java 虚拟机无法解决的严重问题,如 Jvm 系统内部错误、栈内存溢出(代码无法解决的问题) 。
- Exception(程序中的一般问题,可以通过异常处理机制处理的问题):其他因编程错误(类型转换)或偶然的外在因素(用户输入)导致的一般性问题。
- RunTimeException(运行期异常)。
- 异常处理通过五个关键字来实现:
- try(执行可能产生异常的代码):try 是一个独立的代码块,try 中的变量无法在外界使用,需要在外面先声明才可以在外界使用。在 try 中运行到异常后,try 中异常后的代码不会运行,会直接跳到对应的 catch。
- catch(捕获异常,异常发生了必须捕获,否则异常还是存在,有多个 catch 只执行一个):
示例:
try{//可能出现异常的代码
System.out.println(a/b);}catch(ArithmeticException c){//捕获对应类型的异常,在catch中执行处理程序,后续程序不受影响
System.out.println("除数不能为0");}catch(Exception c){//保险,防止有未知的异常,必须写在最下面
c.printStackTrace();}
// 有具体的异常会走具体的,不会走 Exception。
-
- finally(无论是否发生异常,代码总能执行):防止 catch 中出现异常,剩余代码无法执行。
- throws:声明 throws 可能会出现某种异常,异常当前不做处理,交给下一个调用者处理。任何方法都可以使用 throws。调用使用了 throws 的方法时必须处理了声明的异常,要么使用 try-catch 或者继续使用 throws。如果抛出运行期异常(例如:数组越界)没有提示。
- throw:在方法体中,如果不满足某种条件时,主动创建一个对象(有构造方法,可以输出异常原因)抛出异常,方法不在向下执行(相当于 return),使用时最好也使用 throws 让程序员调用时知道这个方法可能会出现异常。作用:用自定义异常标记业务逻辑的异常,避免与标准异常混淆。
- 自定义异常(throw,利用 if 条件语句,当不满足某种情况下创建一个对象抛出异常):自定义异常就是自己定义的一个异常,也就是 api 中异常类的直接或间接的子类。通常创建一个有参的构造方法,用于输出异常原因。
- 如何定义:
- 想要主动提示,继承编译期异常。
- 不需要主动提示,继承运行期异常。
- 为什么要继承 Exception?(要使用父类中的方法)
- 如何定义:
- 编译期异常(其他没有继承 RunTimeException 的类):在代码编译期间,编译器就能明确警示当前代码可能发生异常,并督促程序员提前写好处理代码,如果没有处理,则编译器直接判断编译失败,从而不能生成字节码文件。
- 运行期异常(RunTimeException):在代码编译期间,编译器完全不做任何检查,无论该异常是否发生,编译器都不会给出任何提示,只有等代码运行期间且发生异常,才会被发现(数组越界,空指针)。
- 方法:
- printStackTrace ();// 控制台输出异常原因。
- getMessage ();// 获取异常原因,返回值是字符串。
十九、网络编程
- 三要素:ip 地址,端口号(对应的程序),通信协议。
- 计算机网络:把分布在不同区域具有独立功能的计算机,通过通讯设备与线路连接起来,由功能完善的软件实现资源共享和信息传递的系统。Java 语言是支持网络连接的,从语言级别上支持网络,把网络连接的底层细节封装成一个类,为程序员提供网络类即可。
- 网络编程:开发的程序可以借助网络传输数据,实现数据交换。
- 网络编程中主要的问题:
- 如何定位到计算机网络中的某台主机(ip)和对应的程序(端口)。
- 找到后如何可靠的传输数据(协议)。
- 网络通信模型:
- 应用层(数据产生)。
- 传输层(对数据传输进行控制,协议)。
- 网络层(ip,找到网络中对应的计算机和程序,ip + 端口)。
- 数据链路层(实际通讯设备,光纤,网线)。
- ip:计算机网络地址 (eg:连接到路由器,路由器就会为我们的设备分配一个 ip 地址,此 ip 一般是局域网 ip ),cmd ipconfig 查看局域网 ip。本地回环地址:指向自己的电脑。
- 端口:计算机中应用程序(qq,wx)的一个整数编号,对应每一个程序 ,范围:0-65535。ip 找电脑,端口找程序,ip 和端口都是唯一的。
- 通信协议:通信中的约定,对速率,传输效率,代码,代码结构,出错控制等标准制定。
- tcp(传输控制协议):使用 tcp 发送数据前,必须建立 tcp 连接,会进行三次握手,检测网络是否通畅。如果网络通畅,才进行数据传输。断开时,采用四次挥手机制,是可靠的(安全),但是效率相比 udp 协议低。
- udp(用户数据报协议):将数据源(发送者 ip),目的地(接受者 ip),封装成一个数据包(报),不需要建立连接,直接发送。如果网络有问题,没有任何反馈的,是不可靠的(不安全),由于无须连接发送效率快。
- TCP 协议下的实现类:127.0.0.1 默认本地 ip。
- Socket(客户端)。
- ServerSocket(服务器端)。
- Socket.shutdownOutput ();// 在传输文件时需要调用这个方法,表示文件已经发送完成,注意:OutputStream 对应的是文件还是文字。
- UDP 协议下的实现类:
- DatagramSocket。
- DatagramPacket。