记录一下自学Java基础的知识点,以后还会慢慢补充,先写这么多吧。
JRE是java运行环境,包含JVM和运行时所需要的核心类库(JVM可以保证Java跨平台)
JDK是java程序开发工具包,包含JRE和开发人员使用的工具。其中的开发工具:编译工具(javac.exe)和运行工具(java.exe)
Java内建的package
机制是为了避免class
命名冲突;
JDK的核心类使用java.lang
包,编译器会自动导入;
JDK的其它常用类定义在java.util.*
,java.math.*
,java.text.*
,……;
包名推荐使用倒置的域名,例如org.apache
;
如果有两个class
名称相同,例如,mr.jun.Arrays
和java.util.Arrays
,那么只能import
其中一个,另一个必须写完整类名。
如果有很多.class
文件,散落在各层目录中,肯定不便于管理。如果能把目录打一个包,变成一个文件,就方便多了。jar包就是用来干这个事的,它可以把package
组织的目录层级,以及各个目录下的所有文件(包括.class
文件和其他文件)都打成一个jar文件,这样一来,无论是备份,还是发给客户,就简单多了。jar包实际上就是一个zip格式的压缩文件,而jar包相当于目录。如果我们要执行一个jar包的class
,就可以把jar包放到classpath
中。
除了上述基本类型的变量,剩下的都是引用类型。例如,引用类型最常用的就是String
字符串。引用类型的变量类似于C语言的指针,它内部存储一个“地址”,指向某个对象在内存的位置。
整数运算:
整数运算的结果永远是精确的;
运算结果会自动提升;
可以强制转型,但超出范围的强制转型会得到错误的结果;
应该选择合适范围的整型(int
或long
),没有必要为了节省内存而使用byte
和short
进行整数运算。
浮点数运算:
浮点数常常无法精确表示,并且浮点数的运算结果可能有误差;
比较两个浮点数通常比较它们的差的绝对值是否小于一个特定值;
整型和浮点型运算时,整型会自动提升为浮点型;
可以将浮点型强制转为整型,但超出范围后将始终返回整型的最大值。
字符和字符串:
Java的字符类型char
是基本类型,字符串类型String
是引用类型;
基本类型的变量是“持有”某个数值,引用类型的变量是“指向”某个对象;
引用类型的变量可以是空值null
;
要区分空值null
和空字符串""
。
数组:
数组是同一数据类型的集合,数组一旦创建后,大小就不可变;
可以通过索引访问数组元素,但索引超出范围将报错;
数组元素可以是值类型(如int)或引用类型(如String),但数组本身是引用类型。
遍历数组可以使用for
循环,for
循环可以访问数组索引,for each
循环直接迭代每个数组元素,但无法获取索引;
使用Arrays.toString()
可以快速获取数组内容。
方法的注意事项:
方法的通用格式:
public static 返回值类型 方法名(参数){
方法体;
return 数据;
}
//加法重载
public class MethodDemo{
public static void main(String[] args){
int result1 = sum(10,20);
System.out.println(result1);
double result2 = sum(10.0,20.0);
System.out.println(result2);
int result3 = sum(10, 20, 30);
System.out.println(result3);
}
public static int sum(int a, int b){
return a + b;
}
public static double sum(double a, double b){
return a + b;
}
public static int sum(int a, int b, int c){
return a + b + c;
}
}
对于基本类型的参数,形式参数的改变,不影响实际参数的值
对于引用类型的参数,形式参数的改变,影响实际参数的值(如数组)
System.out.println("内容"); //输出内容并换行
System.out.print("内容"); //输出内容不换行
System.out.println(); //起到换行的作用
//数组遍历方法
public class MethodTest{
public static void main(String[] args){
int arr[] = {11, 22, 33, 44, 55};
printArray(arr);
}
public static void printArray(int arr[]){
System.out.println("[");
for(int x = 0;x<arr.length;x++){
if(x==arr.length-1){
System.out.print(arr[x]);
}else{
System.out.print(arr[x]+", ");
}
System.out.println("]");
}
}
}
类的重要性:是Java程序的基本组成单位,是对现实生活中一类具有共同属性和行为的事物的抽象,确定对象将会拥有的属性和行为。类是对象的数据类型。
对象:客观存在的事物皆为对象,是类的实体
创建对象:类名 对象名 = new 类名();
var关键字:
有些时候,类型的名字太长,写起来比较麻烦。例如:
StringBuilder sb = new StringBuilder(); //语句1
这个时候,如果想要省略变量类型,可以使用var
关键字:
var sb = new StringBuilder(); //语句2
编译期会根据赋值语句自动推断出变量sb
的类型是StringBuilder
。对编译器来说,语句2实际上会自动变成语句1。
成员变量和局部变量的区别:
private关键字:
其他类使用private修饰的成员变量的方法:
get变量名()
方法:用于获取成员变量的值,用public修饰set变量名(参数)
方法,用于设置成员变量的值,用public修饰this关键字:
封装:
提高代码的安全性和复用性
构造方法:
构造方法是一种特殊的方法,因此和普通的方法一样也可以重载
构造方法的作用是创建对象,功能主要是完成对象数据的初始化
构造方法的名字必须和类名一致,在创建对象时被调用
如果没有定义构造方法,系统将给出一个默认的无参构造方法;如果定义了构造方法,系统将不再提供默认的构造方法
如果自定义了带参构造方法,还要使用无参构造方法,就必须再写一个无参构造方法
因此无论是否使用,都要手工书写无参构造方法
String对象的特点:
字符串的比较:
在Java中,判断值类型的变量是否相等,可以使用==
运算符。但是,判断引用类型的变量是否相等,==
表示“引用是否相等”,或者说,是否指向同一个对象。要判断引用类型的变量内容是否相等,必须使用equals()
方法。
字符串是对象,它比较内容是否相同,是通过一个方法来实现的,这个方法是:equals()
StringBuilder:
对字符串进行拼接操作时,每次拼接都会构建一个新的String对象,既耗时又浪费内存空间,而这种操作还不可避免,因此使用java提供的StringBuilder类来解决这个问题
使用时不需要导包,是一个可变(内容可变)的字符串类,可以把它看成是一个容器
public StringBuilder append(任意类型) //添加数据,并返回对象本身
public StringBuilder reverse() //返回相反的字符串序列,也是对象本身
StringBuilder转换为String:
public String toString() // 通过toString()就可以实现把StringBuilder转换为String
String转换为StringBuilder:
public StringBuilder(String s) //通过构造方法就可以实现把String转换为StringBuilder
集合类的特点:提供一种存储空间可变的存储模型,存储的数据容量可以发生改变
ArrayList:
ArrayList构造方法和添加方法:
public ArrayList() //创建一个空的集合对象
public boolean add(E e) // 将指定的元素追加到集合的末尾
public void add(int index,E element) //在此集合中的指定位置插入指定的元素
ArrayList集合常用方法:
public boolean remove(Object o) //删除指定的元素,返回删除是否成功
public E remove(int index) //删除指定索引处的元素,返回被删除的元素
public E set(int index,E element) //修改指定索引处的元素,返回被修改的元素
public E get(int index) //返回指定索引处的元素
public int size() //返回集合中的元素的个数
package myStudent;
/*
学生类
快捷键:右键选择generate或者Alt+Fn+Insert
*/
public class Student {
//学号
private String sid;
//姓名
private String name;
//年龄
private String age;
//居住地
private String address;
public Student() {
}
public Student(String sid, String name, String age, String address) {
this.sid = sid;
this.name = name;
this.age = age;
this.address = address;
}
public String getSid() {
return sid;
}
public void setSid(String sid) {
this.sid = sid;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getAge() {
return age;
}
public void setAge(String age) {
this.age = age;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
}
package myStudent;
import java.util.ArrayList;
import java.util.Scanner;
/*
学生管理系统
*/
public class StudentManager {
public static void main(String[] args) {
//创建集合对象,用于存储学生数据
ArrayList<Student> array = new ArrayList<Student>();
while (true) {
//用输出语句完成主界面的编写
System.out.println("--------欢迎来到学生管理系统--------");
System.out.println("1 添加学生");
System.out.println("2 删除学生");
System.out.println("3 修改学生");
System.out.println("4 查看所有学生");
System.out.println("5 退出");
System.out.println("请输入你的选择: ");
//用Scanner实现键盘录入数据
Scanner sc = new Scanner(System.in);
String line = sc.nextLine();
//用switch语句完成操作的选择
switch (line) {
case "1":
System.out.println("添加学生");
addStudent(array);
break;
case "2":
System.out.println("删除学生");
deleteStudent(array);
break;
case "3":
System.out.println("修改学生");
updateStudent(array);
break;
case "4":
System.out.println("查看所有学生");
findAllStudent(array);
break;
case "5":
System.out.println("谢谢使用");
System.exit(0); //JVM退出
}
}
}
//定义一个方法,用于添加学生信息
public static void addStudent(ArrayList<Student> array) {
//键盘录入学生对象所需要的数据,显示提示要输入何种信息
Scanner sc = new Scanner(System.in);
String sid;
//为了让程序能够回到这里,我们使用循环实现
while (true) {
System.out.println("请输入学生学号: ");
sid = sc.nextLine();
boolean flag = isUsed(array, sid);
if (flag) {
System.out.println("你输入的学号已经被使用,请重新输入");
} else {
break;
}
}
System.out.println("请输入学生姓名: ");
String name = sc.nextLine();
System.out.println("请输入学生年龄: ");
String age = sc.nextLine();
System.out.println("请输入学生居住地: ");
String address = sc.nextLine();
//创建学生对象,把键盘录入的数据赋值给学生对象的成员变量
Student s = new Student();
s.setSid(sid);
s.setName(name);
s.setAge(age);
s.setAddress(address);
//将学生对象添加到集合中
array.add(s);
//给出添加成功提示
System.out.println("添加成功");
}
//定义一个方法,用于查看学生信息
public static void findAllStudent(ArrayList<Student> array) {
//判断集合中是否有数据,如果没有显示提示信息
if (array.size() == 0) {
System.out.println("无信息,请先添加信息再查询");
return; //让程序不再往下执行
}
//显示表头信息
System.out.println("学号\t\t\t\t姓名\t\t年龄\t\t居住地");
//将集合中的数据取出按照对应格式显示学生信息,年龄显示补充“岁”
for (int i = 0; i < array.size(); i++) {
Student s = array.get(i);
System.out.println(s.getSid() + "\t" + s.getName() + "\t\t" + s.getAge() + "岁\t\t" + s.getAddress());
}
}
//定义一个方法,用于删除学生信息
public static void deleteStudent(ArrayList<Student> array) {
//键盘录入要删除学生的学号,显示提示信息
Scanner sc = new Scanner(System.in);
System.out.println("请输入你要删除的学生的学号:");
String sid = sc.nextLine();
int index = -1;
//遍历集合将对应学生对象从集合中删除
for (int i = 0; i < array.size(); i++) {
Student s = array.get(i);
if (s.getSid().equals(sid)) {
index = i;
break;
}
}
if (index == -1) {
System.out.println("该信息不存在,请重新输入");
} else {
array.remove(index);
System.out.println("删除学生成功");
}
}
//定义一个方法,用于修改学生信息
public static void updateStudent(ArrayList<Student> array) {
Scanner sc = new Scanner(System.in);
System.out.println("请输入你要修改的学生的学号: ");
String sid = sc.nextLine();
System.out.println("请输入学生新姓名:");
String name = sc.nextLine();
System.out.println("请输入学生新年龄:");
String age = sc.nextLine();
System.out.println("请输入学生新居住地:");
String address = sc.nextLine();
//创建学生对象
Student s = new Student();
s.setSid(sid);
s.setName(name);
s.setAge(age);
s.setAddress(address);
//遍历集合修改对应的学生信息
for (int i = 0; i < array.size(); i++) {
Student student = array.get(i);
if (student.getSid().equals(sid)) {
array.set(i, s);
break;
}
}
System.out.println("修改学生信息成功");
}
//定义一个方法,对信号是否被使用进行判断
public static boolean isUsed(ArrayList<Student> array, String sid) {
boolean flag = false;
for (int i = 0; i < array.size(); i++) {
Student s = array.get(i);
if (s.getSid().equals(sid)) {
flag = true;
break;
}
}
return flag;
}
}
继承是面向对象三大特征之一,可以使得子类具有父类的属性和方法,还可以在子类中重新定义,追加属性和方法。继承体现的是种属关系,即A是B的一种或者B是A的一种
继承的格式:public class 子类名 extends 父类名{}
继承的好处和弊端:
继承中变量的访问特点(在子类方法中访问一个变量):
super:
继承中构造方法的访问特点:
子类中所有的构造方法默认都会访问父类中无参的构造方法
如果父类中没有无参构造方法,只有带参构造方法,解决办法:
继承中成员方法的访问特点(通过子类对象访问一个方法):
super内存图:
子类中出现了和父类中一模一样的方法声明
**方法重写的应用:**当子类需要父类的功能,而功能主体子类有自己特有内容时,可以重写父类中的方法,这样既沿袭了父类的功能,又定义了子类特有的内容
**@Overridde:**是一个注解,可以帮助我们检查重写方法声明的正确性
包:
其实就是文件夹,作用就是对类进行分类管理
格式:package 包名;
导包:
使用不同包下的类时,使用的时候要写类的全路径,写起来太麻烦了。为了简化带包的操作,Java提供了导包的功能
导包的格式:import 包名;
权限修饰符:
状态修饰符:
final(最终态)
final关键字是最终的意思,可以修饰成员方法,成员变量,类
final修饰的特点:
static(静态)
static关键字是静态的意思,可以修饰成员方法,成员变量
static修饰的特点:
非静态的成员方法
静态的成员方法:
静态成员方法只能访问静态成员
实例字段在每个实例中都有自己的一个独立“空间”,但是静态字段只有一个共享“空间”,所有实例都会共享该字段。对于静态字段,无论修改哪个实例的静态字段,效果都是一样的:所有实例的静态字段都被修改了,原因是静态字段并不属于实例
虽然实例可以访问静态字段,但是它们指向的其实都是Person class
的静态字段。所以,所有实例共享一个静态字段。
因此,不推荐用实例变量.静态字段
去访问静态字段,因为在Java程序中,实例对象并没有静态字段。在代码中,实例对象能访问静态字段只是因为编译器可以根据实例类型自动转换为类名.静态字段
来访问静态对象。
推荐用类名来访问静态字段。可以把静态字段理解为描述class
本身的字段(非实例字段)
调用实例方法必须通过一个实例变量,而调用静态方法则不需要实例变量,通过类名就可以调用。静态方法类似其它编程语言的函数。因为静态方法属于class
而不属于实例,因此,静态方法内部,无法访问this
变量,也无法访问实例字段,它只能访问静态字段。
多态:同一个对象在不同时候表现出来的不同形态。是指,针对某个类型的方法调用,其真正执行的方法取决于运行时期实际类型的方法
多态的前提和体现:
多态中成员访问特点:
这是由于成员方法有重写,而成员变量没有
多态的好处和弊端:
多态中的转型:
在Java中,一个没有方法体的方法应该定义为抽象方法,而类中如果有抽象方法,该类必须定义为抽象类
面向抽象编程的本质就是:
上层代码只定义规范(例如:abstract class Person
);
不需要子类就可以实现业务逻辑(正常编译);
具体的业务逻辑由不同的子类实现,调用者并不关心。
抽象类的特点:
抽象类的成员特点:
成员变量:可以是变量,也可以是常量
构造方法:有构造方法,但是不能实例化,其作用是用于子类访问父类数据的初始化
成员方法:可以有抽象方法:限定子类必须完成某些动作
也可以有非抽象方法:提高代码复用性
接口就是一种公共的规范标准,只要符合标准,大家都可以通用。Java中的接口更多的体现在对行为的抽象
接口的特点:
public interface 接口名{}
public class 类名 implements 接口名{}
接口的成员特点:
成员变量:只能是常量,默认修饰符为:public static final
构造方法:接口没有构造方法,因为接口主要是对行为进行抽象的,是没有具体存在的。
一个类如果没有父类,默认继承自Object类
成员方法:只能是抽象方法,默认修饰符:public abstract
类和接口的关系:
抽象类和接口的区别:
抽象类是对事物的抽象,接口是对行为的抽象
抽象类名作为实参和返回值:
接口名作为实参和返回值:
实现类可以不必覆写default
方法。default
方法的目的是,当我们需要给接口新增一个方法时,会涉及到修改全部子类。如果新增的是default
方法,那么子类就不必全部修改,只需要在需要覆写的地方去覆写新增方法。
default
方法和抽象类的普通方法是有所不同的。因为interface
没有字段,default
方法无法访问字段,而抽象类的普通方法可以访问实例字段。
/*
说英语的接口
*/
public interface SpeakEnglish {
public abstract void speak();
}
//抽象人类
public abstract class Person {
private String name;
private int age;
public Person() {
}
public Person(String name, int age) {
this.name = name;
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public abstract void eat();
}
//抽象运动员类
public abstract class Player extends Person{
public Player() {
}
public Player(String name, int age) {
super(name, age);
}
public abstract void study();
}
//抽象教练类
public abstract class Coach extends Person{
public Coach() {
}
public Coach(String name, int age) {
super(name, age);
}
public abstract void teach();
}
//具体的乒乓球运动员类
public class PingPangPlayer extends Player implements SpeakEnglish {
public PingPangPlayer() {
}
public PingPangPlayer(String name, int age) {
super(name, age);
}
@Override
public void study() {
System.out.println("乒乓球运动员学习如何发球和旋球");
}
@Override
public void eat() {
System.out.println("乒乓球运动员吃大白菜喝小米粥");
}
@Override
public void speak() {
System.out.println("乒乓球运动员说英语");
}
}
//具体的篮球运动员类
public class BasketballPlayer extends Player{
public BasketballPlayer() {
}
public BasketballPlayer(String name, int age) {
super(name, age);
}
@Override
public void study() {
System.out.println("篮球运动员学习如何运球和投篮");
}
@Override
public void eat() {
System.out.println("篮球运动员吃牛肉喝牛奶");
}
}
//具体乒乓球教练类
public class PingPangCoach extends Coach implements SpeakEnglish{
public PingPangCoach() {
}
public PingPangCoach(String name, int age) {
super(name, age);
}
@Override
public void teach() {
System.out.println("乒乓球教练教如何发球和旋球");
}
@Override
public void eat() {
System.out.println("乒乓球教练吃小白菜喝大米粥");
}
@Override
public void speak() {
System.out.println("乒乓球教练说英语");
}
}
//具体篮球教练类
public class BasketballCoach extends Coach{
public BasketballCoach() {
}
public BasketballCoach(String name, int age) {
super(name, age);
}
@Override
public void teach() {
System.out.println("篮球教练教如何运球和投篮");
}
@Override
public void eat() {
System.out.println("篮球教练吃羊肉喝羊汤");
}
}
//测试类
public class PersonDemo {
public static void main(String[] args) {
//创建运动员和教练对象
PingPangPlayer ppp1 = new PingPangPlayer();//无参
ppp1.setName("小白");
ppp1.setAge(20);
System.out.println(ppp1.getName()+","+ppp1.getAge());
ppp1.speak();
ppp1.eat();
ppp1.study();
System.out.println("--------");
PingPangPlayer ppp2 = new PingPangPlayer("大黑",25);//带参
System.out.println(ppp2.getName()+","+ppp2.getAge());
ppp2.speak();
ppp2.eat();
ppp2.study();
System.out.println("--------");
BasketballPlayer bp1 = new BasketballPlayer();//无参
bp1.setName("小黑");
bp1.setAge(30);
System.out.println(bp1.getName()+","+bp1.getAge());
bp1.eat();
bp1.study();
System.out.println("--------");
BasketballPlayer bp2 = new BasketballPlayer("大白",35);//带参
System.out.println(bp2.getName()+","+bp2.getAge());
bp2.eat();
bp2.study();
System.out.println("--------");
BasketballCoach bc = new BasketballCoach();
bc.setName("小明");
bc.setAge(50);
System.out.println(bc.getName()+","+bc.getAge());
bc.teach();
bc.eat();
System.out.println("--------");
PingPangCoach ppc = new PingPangCoach();
ppc.setName("小红");
ppc.setAge(55);
System.out.println(ppc.getName()+","+ppc.getAge());
ppc.speak();
ppc.teach();
ppc.eat();
System.out.println("--------");
System.out.println("测试完毕");
}
}
内部类就是在一个类中定义一个类
内部类的访问特点:
内部类根据在类中定位的位置不同,可以分为:
外部类名.内部类名 对象名 = 外部类对象.内部类对象
匿名内部类(一种特殊的局部内部类):
用static
修饰的内部类和Inner Class有很大的不同,它不再依附于Outer
的实例,而是一个完全独立的类,因此无法引用Outer.this
,但它可以访问Outer
的private
静态字段和静态方法。如果把StaticNested
移到Outer
之外,就失去了访问private
的权限。
//运行结果:Hello, OUTER
public class Main{
public static void main(String[] args){
outer.StaticNested sn = new Outer.StaticNested();
sn.hello();
}
}
class Outer{
private static String NAME = "OUTER";
private String name;
Outer(String name){
this.name = name;
}
static class StaticNested{
void hello(){
System.out.println("Hello," + Outer.NAME);
}
}
}
没有构造方法,要看类的成员是否都是静态的,如果是,通过类名就可以直接调用
包含执行基本数字运算的方法
public static int abs(int a) //返回参数的绝对值
public static double ceil(double a) //返回大于或等于参数的最小double值。等于一个整数
public static double floor(double a) //返回小于或等于参数的最小double值。等于一个整数
public static int round(float a) //按照四舍五入返回最接近参数的int
public static int max(int a,int b) //返回两个int值中的较大值
public static int min(int a,int b) //返回两个int值中的较小值
public static double pow(double a,double b) //返回a的b次幂的值
public static double random() //返回值为double的正值,[0.0,1.0)
包含两个有用的类字段和方法,不能被实例化
public static void exit(int status) //终止当前运行的Java虚拟机,非零表示异常终止
public static long currentTimeMillis() //返回当前时间(以毫秒为单位),可以作为一个计时器用
Object是类层次的根,每个类都可以将Object作为超类。所有类都直接或者间接的继承该类构造方法:public Object()
为什么说子类的构造方法默认访问的是父类的无参构造方法? 因为它们的顶级父类只有无参构造方法
public StringtoString() //返回对象的字符串表示形式。建议所有子类重写该方法,自动生成
public boolean equals(Object obj) //比较对象是否相等。默认比较地址,重写可以比较内容,自动生成
包含关于操作数组的各种方法
public static StringtoString(int[] a) //返回数组的字符串表示形式
public static void sort(int[] a) //按照数字顺序排列指定的数组
工具类的设计思想:
构造方法用private修饰:防止外界创建对象;成员用public static修饰,让外界用类名访问
将基本数据类型封装成对象的好处在于可以在对象中定义更多的功能方法操作该数据
常见的操作之一:用于基本数据类型与字符串之间的转换
int转换为String
public static String valueOf(int i) //返回int参数的字符串表示形式,该方法是String类中的方法
String转换为int:
public static parseInt(String s) //将字符串解析为int类型,该方法是Integer类中的方法
//基本数据类型有:
byte short int long float double char boolean
//对应的包装类分别为:
Byte Short Integer Long Float Double Character Boolean
自动装箱和拆箱:
装箱:把基本数据类型转换为对应的包装类型
拆箱:把包装类类型转换为对应的基本数据类型
在使用包装类类型的时候,如果做操作,最好先判断是否为null。只要是对象,在使用前就必须进行不为null的判断
date类:代表了一个特定的时间,精确到毫秒
public Date() //分配一个Date对象,并初始化,以便它代表它被分配的时间,精确到毫秒
public Date(long date) //分配一个Date对象,并将其初始化为表示从标准基准时间起指定的毫秒数
public long getTime() //获取的是日期对象从1970年1月1日00:00:00到现在的毫秒值
public void setTime(long time) //设置时间,给的是毫秒值
**SimpleDataFormat类:**是一个具体的类,用于以区域设置敏感的方式格式化和解析日期。日期和时间格式由日期和时间模式字符串指定,在日期和时间模式字符串中,从’A’到’Z’以及从’a’到’z’引号的字母被解释为表示日期或时间字符串的组件的模式字母
常用的模式字母及对应关系如下:
y----年
M----月
d----日
H----时
m----分
s----秒
public SimpleDateFormat() //构造一个SimpleDateFormat,使用默认模式和日期格式
public SimpleDateFormat(String pattern) //构造一个SimpleDateFormat使用给定的模式和默认的日期格式
public final String format(Date date) //格式化(从Date到String):将日期格式化成日期/时间字符串
public Date parse(String source) //解析(从String到Date):从给定的字符串的开始解析文本以生成日期
Calendar类:
为某一时刻和一组日历字段之间的转换提供了一些方法,并为操作日历字段提供了一些方法
Calendar提供了一个类方法**getinstance()**用于获取Calendar对象,其日历字段已使用当前日期和时间初始化
public int get(int field) //返回给定日历字段的值
public abstract void add(int field,int amount) //根据日历的规则,将指定的时间量添加或减去给定的日历字段
public final void set(int year,int month,int date) //设置当前的年月日
异常:就是程序出现了不正常的情况
Error:严重问题,不需要处理
Exception:称为异常类,它表示程序本身可以处理的问题
JVM的默认处理方案:
如果程序出现了问题,我们没有做任何处理,最终JVM会做默认的处理:
Throwable的成员方法:
public String getMessage() //返回次throwable的详细消息字符串
public String toString() //返回次可抛出的简短描述
public void printStackTrace() //把异常的错误信息输出在控制台
编译时异常和运行时异常的区别:
Java中的异常被分为两大类:编译时异常和运行时异常,也被称为受检异常和非受检异常。所有的RuntimeException类及其子类被称为运行时异常,其他的异常都是编译时异常
异常处理:
格式:
try {
//可能出现异常的代码;
} catch(异常类名 变量名){
//异常的处理代码;
}
执行流程:
throws(仅仅只是抛出异常,并没有处理异常)
并不是所有的情况我们都有权限进行异常的处理,当出现的异常是我们处理不了的时候,Java提供了throws的处理方法
格式:throws 异常类名;(这个格式是在方法的括号后面的)
编译时异常必须要进行处理,两种处理方案:try…catch…或者throws,如果采用throws这种方案,将来谁调用谁处理
运行时异常可以不处理,出现问题后,需要我们回来修改代码
throws和throw的区别:
throws:用在方法生命后面,跟的是异常类名;
表示抛出异常,由该方法的调用者来处理;
表示出现异常的一种可能性,并不一定会发生这些异常。
throw: 用在方法体内,跟的是异常对象名;
表示抛出异常,由方法体内的语句处理;
执行throw一定抛出了某种异常。
Collection集合概述:
Collection集合常用方法:
boolean add(E e) //添加元素
boolean remove(Object o) //从集合中移除指定的元素
void clear() //清空集合中的元素
boolean contains(Objecct o) //判断集合总是否存在指定的元素
boolean isEmpty() //判断结合是否为空
int size() //集合的长度,也就是集合中元素的个数
Collection集合的遍历:
Iterator:迭代器,集合的专用遍历方式
Iterator中的常用方法:
集合的使用步骤:
public static void main(String[] args){
Collection<String> c = new ArrayList<String>(); //步骤1:创建集合对象
//步骤2:添加元素
String s = "hello world"; //步骤2.1:创建元素
c.add(s); //步骤2.2:添加元素到集合
c.add("hello world"); //合并:添加元素到集合
//步骤3:遍历集合
Iterator<String> it = c.iterator(); //步骤3.1:通过集合对象获取迭代器对象
while(it.hasNext()){ //步骤3.2:通过迭代器对象的hasNext()方法判断是否还有元素
String s = it.next(); //步骤3.3:通过迭代器对象的next()方法获取下一个元素
System.out.println(s);
}
}
List集合概述:
List集合特点:
List集合常用方法:
void add(int index, E element) //在此集合中的指定位置插入指定的元素
E remove(int index) //删除指定索引处的元素,返回被删除的元素
E set(int index,E element) //修改指定索引处的元素,返回被修改的元素
E get(int index) //返回被指定索引处的元素
并发修改异常:
List列表迭代器:
通过List集合的listIterator()方法得到,所以说它是List集合特有的迭代器
用于允许程序员沿任一方向遍历列表的列表迭代器,在迭代期间修改列表,并获取列表中迭代器的当前位置
常用方法:
E next() //返回迭代中的下一个元素
boolean hasNext() //如果迭代具有更多元素,则返回true
E previous() //返回列表中的上一个元素
boolean hasPrevious() //如果此列表迭代器在相反方向遍历列表时具有更多元素,则返回true
void add(E e) //将指定的元素插入列表
增强for循环:
增强for:简化数组和Collection
集合的遍历
Iterable接口
的类允许其对象成为增强型for语句的目标增强for的格式:
for(元素数据类型 变量名:数组或者Collection集合){
//在此处使用变量即可,该变量就是元素
}
//范例:
int[] arr = {1,2,3,4,5};
for(int i:arr){
System.out.println(i);
}
List集合子类特点:
List集合常用子类:ArrayList,LinkedList
ArrayList:底层数据结构是数组,查询快,增删慢
LinkedList:底层数据结构是链表,查询慢,增删快
LinkedList集合的特有方法:
public void addFirst(E e) //在该列表开头插入指定的元素
public void addLast(E e) //将指定的元素追加到此列表的末尾
public E getFirst() //返回此列表中的第一个元素
public E getLast() //返回此列表中的最后一个元素
public E removeFirst() //从此列表中删除并返回第一个元素
public E removeLast() //从列表中删除并返回最后一个元素
数据结构是计算机存储、组织数据的方式,是指相互之间存在一种或多种特定关系的数据元素的集合
通常情况下,精心选择的数据结构可以带来更高的运行或者存储效率
Set集合概述和特点:
哈希值:
哈希值是JDK根据对象的地址或者字符串或者数字算出来的int类型的数值
Object类中有一个方法可以获取对象的哈希值:
public int hashCode() //返回对象的哈希码值
对象的哈希值特点:
HashSet集合概述和特点:
HashSet集合保证元素唯一性:
LinkedHashSet集合概述和特点:
TreeSet集合概述和特点:
自然排序Comparable的使用:
比较器排序Comparator的使用:
泛型概述:
泛型:是JDK5中引入的特性,它提供了编译时类型安全监测机制,该机制允许在编译时检测到非法的类型。它的本质是参数化类型,也就是说所操作的数据类型被指定为一个参数。一提到参数,最熟悉的就是定义方法时有形参,然后调用此方法时传递实参。那么参数化类型怎么理解呢?顾名思义,就是将类型由原来的具体的类型参数化,然后在使用/调用时传入具体的类型。这种参数类型可以用在类、方法和接口中,分别被称为泛型类、泛型方法、泛型接口
泛型定义格式:
泛型的好处:
泛型类:
public class Generic {}
泛型方法:
public void show(T t){ }
泛型接口:
public interface Generic { }
类型通配符:
为了表示各种泛型List的父类,可以使用类型通配符
如果说我们不希望List>是任何泛型的父类,只希望它代表某一类泛型List的父类,可以使用类型通配符的上限
除了可以指定类型通配符的上限,我们也可以指定类型通配符的下限
可变参数:
可变参数又称参数个数可变,用作方法的形参出现,那么方法参数个数就是可变的了
public static int sum(int... a){ }
可变参数注意事项:
可变参数的使用:
//Arrays工具类中有一个静态方法
public static <T> List<T> aList(T... a) //返回由指定数组支持的固定大小的列表,返回的集合不能做增删操作,可以做修改操作
//List接口中有一个静态方法
public static <E> List<E> of(E... elements) //返回包含任意数量元素的不可变列表,返回的集合不能做增删操作
//Set接口中有一个静态方法
public static <E> Set<E> of(E... elements) //返回一个包含任意数量元素的不可变集合,在给元素的时候,不能给重复的元素,返 回的集合不能做增删操作,没有修改的方法
Map集合概述:
Interface Map
K:键的类型;V:值的类型Map集合的基本功能:
V put(K key,V value) //添加元素
V remove(Object key) //根据键删除键值对元素
void clear() //移除所有的键值对元素
boolean containsKey(Object key) //判断集合是否包含指定的键
boolean containsValue(Object value) //判断结合是否包含指定的值
boolean isEmpty() //判断集合是否为空
int size() //集合的长度,也就是集合中键值对的个数
Map集合的获取功能:
V get(Object key) //根据键获取值
Set<K> keySet() //获取所有键的集合
Collection<V> values() //获取所有值的集合
Set<Map.Entry<K,V>> entrySet() //获取所有键值对对象的集合
Collections的概述:
是针对集合操作的工具类
Collections类的常用方法:
public static <T extends Comparable<? super T>> void sort(List<T> list) //将指定的列表按升序排列
public static void reverse(List<?> list) //反转指定列表中元素的顺序
public static void shuffle(List<?> list) //使用默认的随机源随机排列指定的列表
/*
需求:
通过程序实现斗地主过程中的洗牌,发牌和看牌。要求:对牌进行排序
思路:
1.创建HashMap,键是编号,值是牌
2.创建ArrayList,存储编号
3.创建花色数组和点数数组
4.从0开始往HashMap里面存储编号,并存储对应的牌。同时往ArrayList里面存编号
5.洗牌(洗的是编号),用Collections的shuffle()方法实现
6.发牌(发的也是编号,为了保证编号是排序的,创建TreeSet集合接收)
7.定义方法看牌(遍历TreeSet集合,获取编号,到HashMap集合找对应的牌)
8.调用看牌方法
*/
import java.util.ArrayList;
import java.util.Collections;
import java.util.HashMap;
import java.util.TreeSet;
public class PokerDemo {
public static void main(String[] args) {
//创建HashMap,键是编号,值是牌
HashMap<Integer, String> hm = new HashMap<Integer, String>();
//创建ArrayList,存储编号
ArrayList<Integer> array = new ArrayList<Integer>();
//创建花色数组和点数数组
String[] colors = {"♦", "♣", "♠", "♥"};
String[] numbers = {"3", "4", "5", "6", "7", "8", "9", "10", "J", "Q", "K", "A", "2"};
//从0开始往HashMap里面存储编号,并存储对应的牌。同时往ArrayList里面存编号
int index = 0;
for (String number : numbers) {
for (String color : colors) {
hm.put(index, color + number);
array.add(index);
index++;
}
}
hm.put(index, "小王");
array.add(index);
index++;
hm.put(index, "大王");
array.add(index);
//洗牌(洗的是编号),用Collections的shuffle()方法实现
Collections.shuffle(array);
//发牌(发的也是编号,为了保证编号是排序的,创建TreeSet集合接收)
TreeSet<Integer> hyfSet = new TreeSet<Integer>();
TreeSet<Integer> zxxSet = new TreeSet<Integer>();
TreeSet<Integer> ggsqSet = new TreeSet<Integer>();
TreeSet<Integer> dpSet = new TreeSet<Integer>();
for (int i = 0; i < array.size(); i++) {
int x = array.get(i);
if (i >= array.size() - 3) {
dpSet.add(x);
} else if (i % 3 == 0) {
hyfSet.add(x);
} else if (i % 3 == 1) {
zxxSet.add(x);
} else if (i % 3 == 2) {
ggsqSet.add(x);
}
}
//调用看牌方法
lookPoker("胡一菲", hyfSet, hm);
lookPoker("曾小贤", zxxSet, hm);
lookPoker("关谷神奇", ggsqSet, hm);
lookPoker("底牌", dpSet, hm);
}
//定义方法看牌(遍历TreeSet集合,获取编号,到HashMap集合找对应的牌)
public static void lookPoker(String name, TreeSet<Integer> ts, HashMap<Integer, String> hm) {
System.out.print(name + "的牌是:");
for (Integer key : ts) {
String poker = hm.get(key);
System.out.print(poker + "");
}
System.out.println();
}
}
File概述和构造方法
File:它是文件和目录路径名的抽象表示
File(String pathname) //通过给定的路径名字字符串转换为抽象路径名来创建新的File实例
File(String parent,String child) //从父路径名字符串和子路径名字符串创建新的File实例
File(File parent,String child) //从父抽象路径名和子路径名字符串创建新的File实例
File类创建功能:
public boolean createNewFile() //当具有该名称的文件不存在时,创建一个由该抽象路径命名的新空文件
public boolean mkdir() //创建由此抽象路径名命名的目录
public boolean mkdirs() //创建由此抽象路径名命名的目录,包括任何必需但不存在的父目录
File类判断和获取功能:
public boolean isDirectory() //测试此抽象路径名表示的File是否为目录
public boolean isFile() //测试此抽象路径名表示的File是否为文件
public boolean exists() //抽象路径名表示的File是否存在
public String getAbsolutePath() //返回此抽象路径名的绝对路径名字符串
public String getPath() //将此抽象路径名转换为路径名字符串
public String getName() //返回由此抽象路径名表示的文件或目录的名称
public String[] list() //返回此抽象路径名表示的目录中的文件和目录的名称字符串数组
public File[] listFiles() //返回此抽象路径名表示的目录中的文件和目录的File对象数组
File类删除功能:
public boolean delete() //删除由此抽象路径名表示的文件或目录
绝对路径和相对路径的区别
删除目录时的注意事项:如果一个目录中有内容(目录,文件),不能直接删除。应该先删除目录中的内容,然后才能删除目录。
递归:
递归概述:以编程的角度来看,递归指的是方法定义中调用方法本身的现象
递归解决问题的思路:把一个复杂的问题层层转化为一个与原问题相似的规模较小的问题来求解。递归策略只需少量的程序就可描述处解题过程所需要的多次重复运算
递归解决问题要找到两个内容:
IO流概述和分类:
IO流分类:
一般来说我们说IO流的分类是按照数据类型来分的。
如果数据通过windows自带的记事本软件打开我们还可以读懂里面的内容,就使用字符流,否则使用字节流。如果不知道使用哪种类型的流,就使用字节流。
字节流写数据:
字符流抽象基类
InputStream
:这个抽象类是表示字节输入流的所有类的超类OutputStream
:这个抽象类是表示字节输出流的所有类的超类字节流抽象基类
InputStream
:这个抽象类是表示字节输入流一的所有类的超类OutputStream
:这个抽象类是表示字节输出流的所有类的超类FileOutputStream:文件输出流用于将数据写入File
FileOutputStream(String name)
:创建文件输出流以指定的名称写入文件使用字节输出流写数据的步骤:
void write(int b) //将指定的字节写入此文件输出流,一次写一个字节数据
void write(byte[] b) //将b.length字节从指定的字节数组写入此文件输出流,一次写一个字节数组数据
void write(byte[] b,int off,int len) //将len字节从指定的字节数组开始,从偏移量off开始写入此文件输出流一次写一个字节数组 的部分数据
字节流写数据的两个小问题:
字节流写数据实现换行的方法:写完数据后,加换行符 windows:\r\n,Linux:\n,mac:\r
字节流写数据如何实现追加写入呢
public FileOutputStream(String name,boolean append)
字节流读数据:
/*
需求:把文件fos.txt中的内容读取出来在控制台输出
FileInputStream(String name):通过打开与实际文件的连接来创建一个FileInputStream,该文件由文件系统中的路径名name命名
使用字节输入流读数据的步骤:
(1)创建字节输入流对象
(2)调用字节输入流对象的读数据方法
(3)释放资源
*/
byte[] bys = new byte[1024]; //1024及其整数倍
int len;
while((len=fis.read(bys))!=-1){
System.out.println(new String(bys,0,len));
}
字节缓冲流:
BufferedInputStream
将创建一个内部缓冲区数组。当从流中读取或跳过字节时,内部缓冲区将根据需要从所包含的输入流中重新填充,一次很多字节构造方法:
BufferedOutputStream(OutputStream out)
BufferedInputStream(InputStream in)
为什么构造方法需要的是字节流,而不是具体的文件或者路径呢?
**为什么会出现字符流:**由于字节流操作中文不是特别的方便,所以Java就提供字符流
用字节流复制文本文件时,文本文件也会有中文,但是没有问题,原因是最终底层操作会自动进行字节拼接成中文,识别是中文的方法:汉字在存储的时候,无论选择哪种编码存储,第一个字节都是负数
编码表:
基础知识:计算机中存储的信息都是用二进制数表示的;我们在屏幕上看到的英文、汉字等字符都是二进制数转换之后的结果。按照某种规则,将字符存储到计算机中,称为编码。反之,将存储在计算机中的二进制数按照某种规则解析显示出来,称为解码。
字符集:
ASCIl字符集:
GBXXX字符集:
Unicode字符集:
为表达任意语言的任意字符而设计,是业界的一种标准,也称为统一码、标准万国码。它最多使用4个字节的数字来表达每个字母、符号,或者文字。有三种编码方案,UTF-8、UTF-16和UTF32。最为常用的UTF-8编码
UTF-8编码:可以用来表示Unicode标准中任意字符,它是电子邮件、网页及其他存储或传送文字的应用中,优先采用的编码。互联网工程工作小组(IETF)要求所有互联网协议都必须支持UTF-8编码。它使用一至四个字节为每个字符编码
编码规则:
128个US-ASCIl字符,只需一个字节编码
拉丁文等字符,需要二个字节编码
大部分常用字(含中文),使用三个字节编码
其他极少使用的Unicode辅助字符,使用四字节编码
字符串中的编码解码问题:
//编码:
byte[] getBytes() //使用平台的默认字符集将该String
byte[] getBytes(String charsetName) //使用指定的字符集将该String编码为一系列字节,将结果存储到新的字节数组中
//解码:
String(byte[] bytes) //通过使用平台的默认字符集解码指定的字节数组来构造新的String
String(byte[] bytes,String charsetName) //通过指定的字符集解码指定的字节数组来构造新的String
字符流中的编码解码问题:
字符流抽象基类:
Reader
:字符输入流的抽象类Writer
:字符输出流的抽象类字符流中和编码解码问题相关的两个类:
InputStreamReader
OutputStreamWriter
字符流写数据的5种方式:
void write(int c) //写一个字符
void write(char[] cbuf) //写入一个字符数组
void write(cahr[] cbuf,int off,int len) //写入字符数组的一部分
void write(String str) //写一个字符串
void write(String str,int off,int len) //写一个字符串的一部分
flush()
:刷新流,还可以继续写数据
close()
:关闭流,释放资源,但是在关闭之前会先刷新流。一旦关闭,就不能再写数据
字符流读数据的两种方式:
int read() //一次读一个字符数据
int read(char[] cbuf) //一次读一个字符数组数据
字符流读数据和字节流读数据的格式是一样的,只不过一个是字符一个是字节而已
字符缓冲流:
构造方法:
BufferedWriter(Writer out)
BufferedReader(Reader in)
字符冲流特有功能:
//BufferedWriter:
void newLine() //写一行行分隔符,行分隔符字符串由系统属性定义
//BufferedReader:
public String readLine() //读一行文字。结果包含行的内容的字符串,不包括任何行终止字符,如果的结尾已经到达,则为null
复制文件的异常处理:
标准输入输出流:
System类中有两个静态的成员变量:
public static final InputStream in //标准输入流,通常该流对应于键盘输入或由主机环境或用户指定的另一个输入源
public static final PrintStream out //标准输出流,通常该流对应于显示输出或由主机环境或用户指定的另一个输出目标
自己实现键盘录入数据:
BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
//写起来太麻烦,Java就提供了一个类实现键盘录入
Scanner sc = new Scanner(System.in);
输出语句的本质是一个标准的字节输出流
打印流:
打印流分类:
打印流的特点:
字节打印流:
PrintStream(String fileName)
:使用指定的文件名创建新的打印流字符打印流PrintWriter的构造方法:
PrintWriter(String fileName) //使用指定的文件名创建一个新的PrintWriter,而不需要自动执行刷新
PrintWriter(Writer out,boolean autoFlush) //创建一个新的PrintWriter out:字符输出流;autoFlush:一个布尔值,如果为 真,则println,printf,或format方法将刷新输出缓冲区
对象序列化流:
对象序列化:就是将对象保存到磁盘中,或者在网络中传输对象
这种机制就是使用一个字节列表示一个对象,该字节序列包含:对象的类型、对象的数据和对象中存储的属性等信息。字节序列写到文件之后,相当于文件中持久保存了一个对象的信息
反之,该字节序列还可以从文件中读取回去,重构对象,对它进行反序列化
对象序列化流:ObjectOutputStream
构造方法:
ObjectOutputStream(OutputStream out)
:创建一个写入指定的OutputStream的ObjectOutputStream
序列化对象的方法:
void writeObject(Object obj)
:将指定的对象写入ObjectOutputStream
注意:一个对象要想被序列化,该对象所属的类必须实现Serializable接口(Serializable是一个标记接口,实现该接口,不需要重写任何方法)
对象反序列化流:
ObjectInputStream反序列化先前使用ObjectOutputStream编写的原始数据和对象
构造方法:ObjectInputStream(InputStream in)
:创建从指定的InputStream读取的ObjectInputStream
反序列化对象的方法:Object readObject()
:从ObjectInputStream读取一个对象
用对象序列化流序列化一个对象后,假如我们修改了对象所属的类文件,读取数据会不会出问题呢?
如果出问题了,如何解决呢?
private static final long serialVersionUID = 42L
;如果一个对象中的某个成员变量的值不想被序列化,又该如何实现呢?
Properties:
Properties是一个Map体系的集合类,可以保存到流中或从流中加载
Properties作为集合的特有方法:
Object setProperty(String key,String value) //设置集合的键和值,都是String类型,底层调用Hashtable方法put
String getProperty(String key) //使用此属性列表中指定的键搜索属性
Set<String> stringPropertyNames() //从该属性列表中返回一个不可修改的键集,其中键及其对应的值是字符串
Properties和IO流相结合的方法:
void load(InputStream inStream) //从输入字节流读取属性列表(键和元素对)
void load(Reader reader) //从输入字符流读取属性列表(键和元素对)
void store(OutputStream out,String comments) //将此属性列表(键和元素对)写入此Properties表中,以适合于使用 load(InpputStream)方法的格式写入输出字节流
void store(Writer writer,String comments) //将此属性列表(键和元素对)写入此Properties表中,以适合于使用 load(Reader)方法的格式写入输出字节流
进程和线程:
进程 正在运行的程序,是系统进行资源分配和调用的独立单位,每一个进程都有它自己的内存空间和系统资源
线程是进程中的单个顺序控制流,是一条执行路径。分为单线程:一个进程只有一条执行路径,和多线程:一个进程有多条执行路径
多线程实现方式之继承Thread类:
为什么要重写run()方法?因为run()是用来封装被线程执行的代码
run()方法和start()方法的区别?
多线程实现方式之实现Runnable接口:
相比继承Thread类,实现Runnable接口的好处:
设置和获取线程名称:
Thread类中设置和获取线程名称的方法
void setName(String name)
:将此线程的名称更改为等于参数nameString getName()
:返回此线程的名称如何获取main方法所在的线程名称?
public static Thread currentThread() //返回对当前正在执行的线程对象的引用
线程调度:
Java使用的是抢占式调度模型
假如计算机只有一个CPU,那么CPU在某一时刻只能执行一条指令,线程只有得到CPU时间片,也就是使用权,才可以执行指令。所以多线程程序的执行是有随机性,因为谁抢到CPU的使用权是不一定的
Thread类中设置和获取线程优先级的方法:
public final int getPriority() //返回此线程的优先级
public final void setPriority(int newPriority) //更改此线程的优先级
/*
线程默认优先级是5;线程优先级的范围是:1-10
线程优先级高仅仅表示线程获取的CPU时间片的几率高,但是要在次数比较多,或者多次运行的时候才能看到你想要的效果
*/
线程控制:
static void sleep(long millis) //使当前正在执行的线程停留(暂停执行)指定的毫秒数
void join() //等待这个线程死亡
void setDaemon(boolean on) //将此线程标记为守护线程,当运行的线程都是守护线程时,Java虚拟机将退出
线程生命周期:
线程同步,数据安全问题:
判断多线程程序是否会有数据安全问题的标准:
同步代码块:
锁多条语句操作共享数据,可以使用同步代码块实现
格式:
synchronized(任意对象){
//多条语句操作共享数据代码
}
synchronized(任意对象):就相当于给代码枷锁了,任意对象就可以看成是一把锁
同步的好处和弊端
同步方法:
同步方法就是把synchronized关键字加到方法上,格式:修饰符 synchronized 返回值类型 方法名(方法参数) { }
同步方法的锁对象:this
同步静态方法:就是把synchronized关键字加到静态方法上,格式:
修饰符 static synchronized 返回值类型 方法名(方法参数) { }
同步静态方法的锁对象:类名.class
线程安全的类:
StringBuffer
Vector
Hashtable
Lock锁:
虽然我们可以理解同步代码块和同步方法的锁对象问题,但是我们并没有直接看到在哪里加上了锁,在哪里释放了锁,为了更清晰的表达如何加锁和释放锁,JDK5以后提供了一个新的锁对象Lock
Lock实现提供比使用synchronized方法和语句可以获得更广泛的锁定操作Lock中提供了获得锁和释放锁的方法
void lock() //获得锁
void unlock() //释放锁
Lock是接口不能直接实例化,这里采用它的实现类ReentrantLock来实例化ReentrantLock的构造方法:
ReentrantLock)
:创建一个ReentrantLock的实例
生产者消费者:
生产者消费者模式是一个十分经典的多线程协作的模式。所谓生产者消费者问题,实际上主要包含了两类线程:
为了解耦生产者和消费者的关系,通常会采用共享的数据区域,就像是一个仓库
为了体现生产和消费过程中的等待和唤醒,Java提供了几个方法供我们使用,这几个方法在Object类中。Object类的等待和唤醒方法:
void wait() //导致当前线程等待,直到另一个线程调用该对象的notify()方法或notifyAll()方法
void notify() //唤醒正在等待对象监视器的单个线程
void notifyAll() //唤醒正在等待对象监视器的所有线程
网络编程概述:在网络通信协议下,实现网络互连的不同计算机上运行的程序间可以进行数据交换
网络编程三要素:
IP地址
端口
协议
InetAddress的使用:
为了方便我们对IP地址的获取和操作,Java提供了一个类InetAddress供我们使用
InetAddress:此类表示Internet协议(IP)地址
static InetAddress getByName(String host) //确定主机的IP地址。主机名称可以是机器名称,也可以是IP地址
String getHostName() //获取此IP地址的主机名
String getHostAddress() //返回文本显示中的IP地址字符串
端口:
端口是设备上应用程序的唯一标识。端口号是用两个字节表示的整数,它的取值范围是065535。其中01023之间的端口号用于一些知名的网络服务和应用,普通的应用程序需要使用1024以上的端口号。如果端口号被另外一个服务或应用所占用,会导致当前程序启动失败
协议:
UDP协议
TCP协议
UDP收发数据:
//发送数据的步骤
//(1)创建发送端的Socket对象(DatagramSocket)
DatagramSocket()
//(2)创建数据,并把数据打包
DatagramPacket(byte[] buf,int length,InetAddress address,int port)
//(3)调用DatagramSocket对象的方法发送数据
void send(DatagramPacket p)
//(4)关闭发送端
void close()
//接收数据的步骤
//(1)创建接收端的Socket对象(DatagramSocket)
DatagramSocket(int port)
//(2)创建一个数据包,用于接收数据
DatagramPacket(byte[] buf,int length)
//(3)调用DatagramSocket对象的方法接收数据
void receive(DatagramPacket p)
//(4)解析数据包,并把数据在控制台显示
byte[] getData()
int getLength()
//(5)关闭接收端
void close()
TCP通信:
TCP通信原理:
TCP通信协议是一种可靠的网络协议,它在通信的两端各建立一个Socket对象,从而在通信的两端形成网络虚拟链路,一旦建立了虚拟的网络链路,两端的程序就可以通过虚拟链路进行通信
Java对基于TCP协议的的网络提供了良好的封装,使用Socket对象来代表两端的通信端口,并通过Socket产生I0流来进行网络通信
Java为客户端提供了Socket类,为服务器端提供了ServerSocket类
TCP收发数据:
//发送数据的步骤
//(1)创建客户端的Socket对象(Socket)
Socket(String host,int port)
//(2)获取输出流,写数据
OutputStream getOutputStream()
//(3)释放资源
void close()
//接收数据的步骤
//(1)创建服务器端的Socket对象(ServerSocket)
ServerSocket(int port)
//(2)监听客户端连接,返回一个Socket对象
Socket accept()
//(3)获取输入流,读数据,并把数据显示在控制台
InputStream getInputStream()
//(4)释放资源
void close()
面向对象思想强调“必须通过对象的形式来做事情”,函数式思想则尽量忽略面向对象的复杂语法:“强调做什么,而不是以什么形式去做”。而Lambda表达式就是函数式思想的体现
Lambda表达式的格式:
new Thread(()->{
System.out.println("多线程程序启动了");
}).start();
分析:
Lambda表达式的格式:
Lambda表达式的省略规则:
Lambda表达式的注意事项:
Lambda表达式和匿名内部类的区别:
接口的组成:
public static final
public abstract
接口中默认方法:
接口中默认方法的定义格式:public default 返回值类型 方法名(参数列表) { }
注意事项:默认方法不是抽象方法,所以不强制被重写,但是可以被重写,重写的时候去掉default关键字;public可以省略,default不能省略
接口中静态方法:
接口中静态方法的定义格式:public static void 返回值类型 方法名(参数列表) { }
注意事项:静态方法只能通过接口名调用,不能通过实现类型或者对象名调用;public可以省略,static不能省略
接口中私有方法:
Java 9中新增了带方法体的私有方法,这其实在Java8中就埋下了伏笔: Java8允许在接口中定义带方法体的默认方法和静态方法。这样可能就会引发一个问题:当两个默认方法或者静态方法中包含一段相同的代码实现时,程序必然考虑将这段实现代码抽取成一个共性方法,而这个共性方法是不需要让别人使用的,因此用私有给隐藏起来,这就是Java 9增加私有方法的必然性
接口中私有方法的定义格式:
//格式1: private返回值类型方法名(参数列表){}
private void show) { }
//格式2: private static返回值类型方法名(参数列表){}
private static void method({}
/*
接口中私有方法的注意事项:
默认方法可以调用私有的静态方法和非静态方法
静态方法只能调用私有的静态方法
*/
在使用Lambda表达式的时候,我们实际上传递进去的代码就是一种解决方案:拿参数做操作。如果我们在Lambda中所指定的操作方案,已经有地方存在相同方案,那就没有必要再写重复逻辑。我们通过方法引用来使用已经存在的方案
方法引用符:
-方法引用符 :: 该符号为引用运算符,而它所在的表达式被称为方法引用
回顾一下我们在体验方法引用中的代码
Lambda表达式:usePrintable(s-> System.out.printIn(s));
分析:拿到参数s 之后通过Lambda表达式,传递给System.out.println方法去处理
方法引用:usePrintable(System.out:printIn);
分析:直接使用System.out中的printIn方法来取代Lambda,代码更加的简洁
推导与省略
如果使用Lambda,那么根据“可推导就是可省略”的原则,无需指定参数类型,也无需指定的重载形式,它们都将被自动推导
如果使用方法引用,也是同样可以根据上下文进行推导
方法引用是Lambda的孪生兄弟
Lambda表达式支持的方法引用:
函数式接口:有且仅有一个抽象方法的接口。Java中的函数式编程体现就是Lambda表达式,所以函数式接口就是可以适用于Lambda使用的接口。只有确保接口中有且仅有一个抽象方法,Java中的Lambda才能顺利地进行推导
监测一个接口是函数式接口的方法:
(注解)@FunctionalInterface,放在接口定义的上方:如果接口是函数式接口,编译通过;如果不是,编译失败
注意:我们自己定义函数式接口的时候,@FunctionalInterface是可选的,就算不写这个注解,只要保证满足函数式接口定义的条件,也照样是函数式接口。但是建议加上该注解
**函数式接口作为方法的参数:**如果方法的参数是一个函数式接口,我们可以使用Lambda表达式作为参数传递
**函数式接口作为方法的返回值:**如果方法的返回值是一个函数式接口,我们可以使用Lambda表达式作为结果返回
常用的函数式接口:
Supplier接口
Supplier:包含一个无参的方法
T get():获得结果
该方法不需要参数,它会按照某种实现逻辑(由Lambda表达式实现)返回一个数据
Supplier接口也被称为生产型接口,如果我们指定了接口的泛型是什么类型,那么接口中的get方法就会生产什么类型的数据供我们使用
Consumer接口
Consumer:包含两个方法
void accept(T t)
:对给定的参数执行此操作
default Consumer
:返回一个组合的Consumer,依次执行此操作,然后执行after操作
Consumer接口也被称为消费型接口,它消费的数据的数据类型由泛型指定
Predicate接口
Predicate:常用的四个方法
boolean test(T t)
:对给定的参数进行判断(判断逻辑由Lambda表达式实现),返回一个布尔值
default Predicate
:返回一个逻辑的否定,对应逻辑非
default Predicate
:返回一个组合判断,对应短路与
default Predicate
:返回一个组合判断,对应短路或
Predicate
接口通常用于判断参数是否满足指定的条件
Function接口
Function
R apply(Tt)
:将此函数应用于给定的参数
default
:返回一个组合函数,首先将该函数应用于输入,然后将after函数应用于结果
Function
接口通常用于对参数进行处理,转换(处理逻辑由Lambda表达式实现),然后返回一个新的值
Stream流的使用:
Stream流的生成方式:
Stream流的常见生成方式
Collection体系的集合可以使用默认方法stream()生成流:default Stream
Map体系的集合间接的生成流·
数组可以通过Stream接口的静态方法of(T… values)生成流·
Stream流的常见中间操作方法:
Stream<T> filter(Predicate predicate) //用于对流中的数据进行过滤
//Predicate接口中的方法对给定的参数进行判断,返回一个布尔值 boolean test(T t)
Stream<T> limit(long maxSize) //返回此流中的元素组成的流,截取前指定参数个数的数据
Stream<T> skip(long n) //跳过指定参数个数的数据,返回由该流的剩余元素组成的流
static <T> Stream<T> concat(Stream a, Stream b) //合并a和b两个流为一个流
Stream<T> distinct() //返回由该流的不同元素(根据Objectequals(Object))组成的流
Stream<T> sorted() //返回由此流的元素组成的流,根据自然顺序排序
Stream<T> sorted(Comparator comparator) //返回由该流的元素组成的流,根据提供的Comparator进行排序
<R> Stream<R> map(Function mapper) //返回由给定函数应用于此流的元素的结果组成的流
//Function接口中的方法 R apply(T t)
IntStream mapToInt(ToIntFunction mapper) //返回一个IntStream其中包含将给定函数应用于此流的元素的结果 IntStream:表示原始int流
//TolntFunction接口中的方法 int applyAsInt(T value)
Stream流的常见终结操作方法:
void forEach(Consumer action) //对此流的每个元素执行操作 Consumer接口中的方法
//void accept(T t) 对给定的参数执行此操作
long count() //返回此流中的元素数
Stream流的收集操作:
对数据使用Stream流的方式操作完毕后,我想把流中的数据收集到集合中,该怎么办呢?
Stream流的收集方法
R collect(Collector collector)
但是这个收集方法的参数是一个Collector接口
工具类Collectors提供了具体的收集方式
public static <T> Collector toList() //把元素收集到List集合中
public static <T> Collector toSet() //把元素收集到Set集合中
public static Collector toMap(Function keyMapper,Function valueMapper) //把元素收集到Map集合中
类加载:
当程序要使用某个类时,如果该类还未被加载到内存中,则系统会通过类的加载,类的连接,类的初始化这三个步骤来对类进行初始化。如果不出现意外情况,JVM将会连续完成做三个步骤,所以有时也把这三个步骤统称为类加载或者类初始化
类的加载
就是指将class文件读入内存,并为之创建一个java.lang.Class对象
任何类被使用时,系统都会为之建立一个java.lang.Class对象
类的连接
验证阶段:用于检验被加载的类是否有正确的内部结构,并和其他类协调一致
准备阶段:负责为类的类变量分配内存,并设置默认初始化值
解析阶段:将类的二进制数据中的符号引用替换为直接引用
类的初始化
类的初始化步骤和时机:
类的初始化步骤:
假如类还未被加载和连接,则程序先加载并连接该类
假如该类的直接父类还未被初始化,则先初始化其直接父类
假如类中有初始化语句,则系统依次执行这些初始化语句
注意:在执行第2个步骤的时候,系统对直接父类的初始化步骤也遵循初始化步骤1-3
类的初始化时机:
创建类的实例
调用类的类方法
访问类或者接口的类变量,或者为该类变量赋值
使用反射方式来强制创建某个类或接口对应的java.lang.Class对象
初始化某个类的子类
直接使用java.exe命令来运行某个主类、
类加载器的作用:
负责将.class文件加载到内存中,并为之生成对应的java.lang.Class对象
虽然我们不用过分关心类加载机制,但是了解这个机制我们就能更好的理解程序的运行
JVM的类加载机制
全盘负责:就是当一个类加载器负责加载某个Class时,该Class所依赖的和引用的其他Class也将由该类加载器负责载入,除
非显式使用另外一个类加载器来载入
父类委托:就是当一个类加载器负责加载某个Class时,先让父类加载器试图加载该Class,只有在父类加载器无法加载该类
时才尝试从自己的类路径中加载该类
缓存机制:保证所有加载过的Class都会被缓存,当程序需要使用某个Class对象时,类加载器先从缓存区中搜索该Class,只
有当缓存区中不存在该Class对象时,系统才会读取该类对应的二进制数据,并将其转换成Class对象,存储到缓存区
内置类加载器:
ClassLoader
:是负责加载类的对象
Java运行时具有以下内置类加载器
Bootstrap class loader
:它是虚拟机的内置类加载器,通常表示为null,并且没有父null
Platform class loader
:平台类加载器可以看到所有平台类,平台类包括由平台类加载器或其祖先定义的Java SE平台API,其实现类和JDK特定的运行时类
System class loader
:它也被称为应用程序类加载器,与平台类加载器不同。系统类加载器通常用于定义应用程序类路径,模块路径和JDK特定工具上的类
类加载器的继承关系: System的父加载器为Platform,而Platform的父加载器为Bootstrap
ClassLoader中的两个方法
static ClassLoader getSystemClassLoader() //返回用于委派的系统类加载器
ClassLoader getParent() //返回父类加载器进行委派
反射概述:
Java反射机制:是指在运行时去获取一个类的变量和方法信息,然后通过获取到的信息来创建对象,调用方法的一种机制。由于这种动态性,可以极大的增强程序的灵活性,程序不用在编译期就完成确定,在运行期仍然可以扩展。
获取Class类的对象:
我们要想通过反射去使用一个类,首先我们要获取到该类的字节码文件对象,也就是类型为Class类型的对象
这里我们提供三种方式获取Class类型的对象
使用类的class属性
来获取该类对应的Class对象。举例: Studentclass将会返回Student类对应的Class对象
调用对象的getClass()
方法,返回该对象所属类对应的Class对象.该方法是Object类中的方法,所有的Java对象都可以调用该方法
使用Class类中的静态方法forName(String className)
,该方法需要传入字符串参数,该字符串参数的值是某个类的全路径,也就是完整包名的路径
反射获取构造方法并使用:
//Class类中用于获取构造方法的方法
Constructor<?>[] getConstructors() //返回所有公共构造方法对象的数组
Constructor<?>[] getDeclaredConstructors() //返回所有构造方法对象的数组
Constructor<T> getConstructor(Class<?>...parameterTypes) //返回单个公共构造方法对象
Constructor<T> getDeclaredConstructor(Class<?>...parameterTypes) //返回单个构造方法对象
//Constructor类中用于创建对象的方法
T newInstance(Object... initargs) //根据指定的构造方法创建对象
反射获取成员变量并使用:
//Class类中用于获取成员变量的方法
Field[] getFields() //返回所有公共成员变量对象的数组
Field[] getDeclaredFields() //返回所有成员变量对象的数组
Field getField(String name) //返回单个公共成员变量对象
Field getDeclaredField(String name) //返回单个成员变量对象
//Field类中用于给成员变量赋值的方法
void set(Object obj, Object value) //给obj对象的成员变量赋值为value
反射获取成员方法并使用:
//Class类中用于获取成员方法的方法
Method[] getMethods() //返回所有公共成员方法对象的数组,包括继承的
Method[] getDeclaredMethods() //返回所有成员方法对象的数组,不包括继承的
Method getMethod(String name, Class<?>... parameterTypes) //返回单个公共成员方法对象
Method getDeclaredMethod(String name, Class<?>... parameterTypes) //返回单个成员方法对象
//Method类中用于调用成员方法的方法
Object invoke(Object obj, Object..args) //调用obj对象的成员方法,参数是args,返回值是Object类型