java


java

环境

Java运行命令为:

nohup java –jar 文件名.jar –-参数1-参数2 &

tomacat

nohup ./startup.sh &

第一种方法,将编译好的项目重新 copy 到 webapps 目录下,多出了两步操作。

第二种方法直接在 server.xml 文件中配置,但是从 tomcat5.0版本开始后,server.xml 文件作为 tomcat 启动的主要配置文件,一旦 tomcat 启动后,便不会再读取这个文件,因此无法再 tomcat 服务启动后发布 web 项目。

第三种方法是最好的,每个项目分开配置,tomcat 将以\conf\Catalina\localhost 目录下的 xml 文件的文件名作为 web 应用的上下文路径,而不再理会 中配置的 path 路径,因此在配置的时候,可以不写 path。

通常我们使用第三种方法.

JDK、JRE、JVM

JDK:java开发工具包

JRE:java运行时环境

JVM:java虚拟机 说明: 1、JDK和JRE都可以单独安装,若不需要开发Java程序,只需要运行的话,那么,只用安装JRE即可。 2、JVM不能单独安装。 以下为三者关系图:

1png
1png

字符编码

  • ASCII码:采用一个字节编码,主要针对英文编码

  • GBK:主要是简体汉字编码

  • Unicode:统一了全世界的所有文字编码,采用这三种方式实现(UTF-8、UTF-16、UTF-32) 说明: 1、Java采用Unicode编码方式 2、在实际开发中,一般使用UTF-8编码方式实现

    Java中的命名规则

    包:实质上是文件夹,用于保证类名的唯一性(区分相同类名)。为了保证包名的唯一性,一般采用公司域名以逆序的形式作为包名,然后对于不同的工程使用不同的子包(域名.项目名.模块名)例:com.horstmann.corejava,且包名全部小写 类或接口:大驼峰命名法 方法或变量:小驼峰命名法 常量:一个单词组成时全部大写,多个单词组成时每个单词大写且单词间用_隔开 二进制数:以0b开头 八进制数:以0开头 十六进制数:以0x开头

    有符号数据表示法
    • 原码:二进制定点表示法,即最高位为符号位,“0”表示正,“1”表示负,其余位数表示数值大小
    • 反码:正数的反码与其原码相同,负数的反码是对其原码逐位取反,但符号位除外
    • 补码:正数的补码与其原码相同,负数的补码是在其反码的末位加1

数据类型

  • Java程序中最基本的单位是类
  • Java中变量赋值时必须类型对应,否则不兼容,编译不过,可用强转
  • Java中局部变量不赋值不能使用
  • 2
    2

说明:整数默认为int,浮点数默认为double,布尔类型默认为false

数据类型取值范围

3
3

数据类型默认转换

byteshortchar-->int-->long-->float-->double

Java变量类型:

  • **局部变量(Local Variables):**定义在方法、构造方法或语句块中的变量,作用域只限于当前方法、构造方法或语句块中。局部变量必须在使用前声明,并且不能被访问修饰符修饰。

  • **成员变量(Instance Variables):**定义在类中、方法之外的变量,作用域为整个类,可以被类中的任何方法、构造方法和语句块访问。成员变量可以被访问修饰符修饰。

  • **静态变量(Class Variables):**定义在类中、方法之外的变量,并且使用 static 关键字修饰,作用域为整个类,可以被类中的任何方法、构造方法和语句块访问,静态变量的值在程序运行期间只有一个副本。静态变量可以被访问修饰符修饰。

  • **参数变量(Parameters):**方法定义时声明的变量,作为调用该方法时传递给方法的值。参数变量的作用域只限于方法内部。运行以上代码,输出如下:

  • 
    public class RunoobTest {
        // 成员变量
        private int instanceVar;
        // 静态变量
        private static int staticVar;
        
        public void method(int paramVar) {
            // 局部变量
            int localVar = 10;
            
            // 使用变量
            instanceVar = localVar;
            staticVar = paramVar;
            
            System.out.println("成员变量: " + instanceVar);
            System.out.println("静态变量: " + staticVar);
            System.out.println("参数变量: " + paramVar);
            System.out.println("局部变量: " + localVar);
        }
        
        public static void main(String[] args) {
            RunoobTest v = new RunoobTest();
            v.method(20);
        }
    }
    
    

    运行以上代码,输出如下:

    成员变量: 10
    静态变量: 20
    参数变量: 20
    局部变量: 10
    
    Java 参数变量

    Java 中的参数变量是指在方法或构造函数中声明的变量,用于接收传递给方法或构造函数的值。参数变量与局部变量类似,但它们只在方法或构造函数被调用时存在,并且只能在方法或构造函数内部使用。

    accessModifier returnType methodName(parameterType parameterName1, parameterType parameterName2, ...) {
        // 方法体
    }
    
    • parameterType -- 表示参数变量的类型。
    • parameterName -- 表示参数变量的名称。

    在调用方法时,我们必须为参数变量传递值,这些值可以是常量、变量或表达式。

    方法参数变量的值传递方式有两种:值传递引用传递

    • **值传递:**在方法调用时,传递的是实际参数的值的副本。当参数变量被赋予新的值时,只会修改副本的值,不会影响原始值。Java 中的基本数据类型都采用值传递方式传递参数变量的值。
    • **引用传递:**在方法调用时,传递的是实际参数的引用(即内存地址)。当参数变量被赋予新的值时,会修改原始值的内容。Java 中的对象类型采用引用传递方式传递参数变量的值
    Java 局部变量
    • 局部变量声明在方法、构造方法或者语句块中。
    • 局部变量在方法、构造方法、或者语句块被执行的时候创建,当它们执行完成后,变量将会被销毁。
    • 局部变量必须在使用前声明,并且不能被访问修饰符修饰,因为它们的作用域已经被限制在了声明它们的方法、代码块或构造函数中。
    • 局部变量只在声明它的方法、构造方法或者语句块中可见,不能被其他方法或代码块访问。
    • 局部变量是在栈上分配的。
    • 局部变量没有默认值,所以局部变量被声明后,必须经过初始化,才可以使用。

数组

一维数组

格式如下:

/动态初始化/ 数组类型[] 数组名 = new 数组类型[元素个数(数组长度)];

/静态初始化/ 数组类型[] 数组名 = {元素1, 元素2, 元素3, .....} = new 数组类型[]{元素1, 元素2, 元素3, .....};

/*动态初始化*/
数组类型[] 数组名 = new 数组类型[元素个数(数组长度)];

/*静态初始化*/
数组类型[] 数组名 = {元素1, 元素2, 元素3, .....} = new 数组类型[]{元素1, 元素2, 元素3, .....};

举例:

String[] array1 = {"abc", "a", "ad"};//字符串数组中不能有字符('') char[] array2 = {'A', 'b'};//字符数组中不能有字符串("") int[] array3 = {23, 12, 90};

String[] array1 = {"abc", "a", "ad"};//字符串数组中不能有字符('')
char[] array2 = {'A', 'b'};//字符数组中不能有字符串("")
int[] array3 = {23, 12, 90}; 

说明:以上例子中可以把“[ ]”放在数组名后面(array1[]),但是Java中不建议这样,放在数组名前面更易于理解。

  • 用“ 数组名.length ”的方式来获得数组长度
  • 字符数组默认初始值为一个空字符(“\u000”)
  • boolean数组默认初始值为false,其余数组默认值为0
  • Java中的数组必须先初始化(赋值后)才能使用

二维数组

格式如下:

/*动态初始化*/
写法一:数据类型[][] 数组名 = new 数据类型[m][n];//m表示这个二维数组有m个一维数组, n表示每一个一维数组有n个元素
写法二:数据类型 数组名[][] = new 数据类型[m][n];
写法三:数据类型[] 数组名[] = new 数据类型[m][n];
数据类型[][] 数组名 = new 数据类型[m][];//如果没有给出每个一维数组的元素个数,就表示这个二维数组是变化的,但不能没有一维数组的个数

/*静态初始化*/
数据类型[][] 数组名 = {{元素...}, {元素...}, {元素...}......} = new 数据类型[][] {{元素...}, {元素...}, {元素...}......};

/*注明*/
int[] x, y[];//x是一维数组,y是二维数组

类的初始化过程

以下代码在内存中做了哪些事情?

Student s = new Student();

分五步:

  1. 加载Student.class文件进内存

  2. 在栈内存中为引用变量s开辟空间

  3. 在堆内存中为学生对象开辟空间

  4. 对学生对象的成员变量进行默认初始化

  5. 对学生对象的成员变量进行显示初始化

对上述语句的解析:

​右边的“new Student()”是以Student类为模板,在堆空间里创建一个Student对象 末尾的“()”意味着在对象创建后立即用Student类的构造方法对刚生成的对象进行初始化 左边的“Student s ”创建了一个Student类引用变量,它存在于栈空间中,也就是用来指向Student对象的对象引用 “=”操作符使对象引用指向刚创建的那个Student对象

类的加载

当程序要使用某个类时,如果该类还未被加载到内存中,则系统会通过加载、连接、初始化三步来实现对这个类进行的加载

加载:将.class文件读入内存,并为之创建一个class对象,任何类被使用时系统都会建立一个class对象连接:

验证:是否有正确的内部结构,并和其他类协调一致 准备:负责为类的静态成员分配内存,并设置默认初始值 解析:将类的二进制数据中的符号引用替换为直接引用

类的加载时机

什么时候类会加载?

  • 创建类的实例
  • 访问类的静态变量,或者为静态变量赋值
  • 调用类的静态方法
  • 使用反射方式来强制创建某个类或接口对应的java.lang.class对象
  • 初始化某个类的子类
  • 直接使用java.exe命令来运行某个主类
类加载器

根类加载器(Bootstrap ClassLoader):也叫引导类加载器,负责Java核心类的加载,比如System、String等,在JDK中JRE的lib目录下的rt.jar文件中 扩展类加载器(Extension ClassLoader):负责JRE的扩展目录中jar包的加载,在JDK中JRE的lib目录下的ext目录中 系统类加载器(System ClassLoader):负责在JVM启动时加载来自Java命令的class文件,以及classpath环境变量所指定的jar包和类路径(我们自定义的类就是通过它加载的)

方法的内存分配与变化

方法只定义不调用是不会执行的,并且在JVM中也不会给方法分配“运行所属”的内存空间,只有在调用方法时才会动态的给这个方法分配所属的内存空间 JVM内存划分上有这三块主要的内存空间:方法区内存、栈内存、堆内存 方法代码片段属于.class字节码文件的一部分,字节码文件在类加载的时候被放到了方法区当中,所以JVM中的三块主要的内存空间中方法区内存最先有数据——方法代码片段 栈内存中分配方法运行的所属内存空间 方法在调用的瞬间,给该方法分配内存空间,在栈中发生压栈动作,方法调用结束之后,给该方法分配的内存空间全部释放,此时发生弹栈动作 局部变量运行阶段内存在栈中分配 4

main方法的格式讲解
public static void main(String[] args)
{
	.....
}

public:公共的,访问权限最大的修饰符,由于main方法是被JVM调用,所以权限要足够大 static:静态的,不需要创建对象,方便JVM调用 void:被JVM调用,不需要给JVM返回值 main:被JVM识别,程序的执行入口 String[] args:早期是为了接收键盘录入的数据所设置的参数,现在用Scanner了

构造器

构造器是一种特殊的方法(构造方法),用来构造并初始化对象 构造器与类同名,每个类可以有一个以上的构造器,构造器分无参构造和带参构造 构造器没有返回值 构造器总是伴随着new操作符一起调用 对于一个类来说,当你没有写无参构造方法时,系统会默认给出(因为所有类都继承Object类),写了无参构造方法后系统就不会给了,但两者效果一样,唯独当你只写了带参构造方法时,那么,这个类就没有无参构造方法了

变量
5
5

成员变量与局部变量

两者区别:

在类中位置不同:成员变量在方法外。局部变量在方法内或者方法声明上 在内存中的位置不同:成员变量在堆内存中。局部变量在栈内存中 生命周期不同:成员变量随着对象的创建而存在,随着对象的消失而消失。局部变量随着方法的调用而存在,随着方法调用完毕而消失 初始化值不同:成员变量有默认的初始值(被final修饰的实例变量必须手动赋值,不能采用系统默认值)。局部变量没有默认的初始值,必须在定义赋值后才能使用

注:局部变量名可以与实例变量名相同,在方法被调用时采用就近原则

封装

封装:隐藏对象的属性和实现细节,仅对外提供公共访问方法 封装是一种信息隐藏技术,在java中通过关键字private实现封装 封装的好处:1、提高了代码的复用性和安全性;2、保证内部结构的安全 封装的原则:屏蔽复杂,暴露简单

访问控制权限修饰符
6
6

简单修饰符归纳

类的修饰符可有:public、缺省、final、abstract 注:成员内部类可有private和static修饰符,其余的普通类不行,因为成员内部类可以看作是外部类的成员

成员变量的修饰符可有:private、缺省、protected、public、static、final

构造方法的修饰符可有:private、缺省、protected、public

成员方法的修饰符可有:private、缺省、protected、public、static、final、abstract类

关键字

this关键字

this关键字指向的是当前类的对象的引用,用来区分同名称的成员变量和局部变量 this只能在类中的非静态方法中使用,静态方法和静态代码块中绝对不能出现this,并且this只和特定的对象关联,而不和类关联,同一类的不同对象有不同的this this关键字有两个含义:1、指示隐式参数的引用;2、调用该类的其它构造器

static关键字

static(静态关键字),可以修饰成员变量和成员方法 static关键字的特点:1、随着类的加载而加载;2、优先于对象存在;3、被类的所有对象共享(判断是否使用静态关键字static的条件);4、可以通过类名调用(推荐使用) static方法:static方法也叫作静态方法,由于静态方法不依赖于任何对象就可以进行访问,因此对于静态方法来说是没有this关键字的使用的,因为this关键字要依附于对象,在静态方法中不能访问类的非静态成员,但是,在非静态成员方法中是可以访问静态成员方法或静态变量的 static变量:static变量也叫作静态变量或类变量,静态变量与非静态变量的区别在于:静态变量被所有对象共享,在内存中只有一个副本,它当且仅当在类初次加载时会被初始化;非静态变量是对象所拥有的,在创建对象的时候被初始化,在内存中存在多个副本,且各对象拥有的副本互不影响。static成员变量的初始化顺序按照定义的顺序进行 注: Java中的static关键字不会影响到变量或者方法的作用域,在Java中能够影响访问权限的只有访问权限修饰符 静态成员变量虽然独立于对象,但是仍然可以通过对象进行访问(只要访问权限足够) static是不允许用来修饰局部变量的,可以理解为静态元素与对象没有关系,它属于类,静态元素中不可以使用this、super关键字,静态变量存储在方法区中的静态区

final关键字

final关键字可以用来修饰类、方法和变量
修饰类:当用final修饰一个类时表明这个类不能被继承,final类中的成员变量并不是默认final修饰的,可以根据需要设为final,但final类中的所有方法却默认final修饰
修饰方法:对于重写问题而言,当父类中的某个方法被final修饰时,就表明父类中的这个方法不能被子类重写,也就是禁止子类重写此方法(主要目的是防止该方法的内容被修改)
注:重写的前提是子类可以从父类中继承此方法,如果父类中final修饰的方法同时又被private修饰,此时不会产生重写与final的矛盾,因为子类根本就没有继承这个方法,这个方法被私有化了,既然没有继承何来的重写,final就无作用了,所以当父类中的某个方法的修饰符上同时有private和final时,在子类中依然可以出现同样声明的方法,因为这被视为在子类中重新定义了新的方法
修饰变量:被final修饰的变量就变成了常量,常量不能被重新赋值,只读不可写,Java中定义常量时一般会加上static修饰,因为常量是不变的,任何对象拥有的都一样,如果一直new一样的东西就会浪费内存
注:被定义为final的成员变量必须在构造对象时就被初始化,并且以后不能再修改
final修饰基本数据类型时是值不能被改变,而final修饰引用类型数据时是地址值不能被改变,但是该对象的内容是可以变的
final修饰的实例变量必须手动赋值不能采用系统默认值
父类中的final方法可被子类继承,但是不能被子类重写
final修饰的引用指向的对象无法被垃圾回收器回收

注:当变量被final修饰后,这个变量就变成了常量,既然是常量,那么它在内存中存储的就只是数值了,与之前的变量内存就无关系了,即当变量消失时,常量不会消失,依旧是那个数值在运算,所以,若想某个数据不会因变量消失而消失,就将它修饰为常量

代码块

在Java中使用{ }括起来的代码被称为代码块,根据其位置和声明的不同可以分为:局部代码块、构造代码块、静态代码块、同步代码块

局部代码块:其又叫普通代码块,在方法中出现,限定变量生命周期,主要用于解决当前方法中变量名重复的问题。若想要在一个方法中多次使用同一个变量名,并且互不影响,这时就可以将该变量放入不同局部代码块当中,因局部代码块中的变量生命周期只限于该代码块中 构造代码块:在类中方法外出现,多个构造方法中相同的代码存放到一起,每次调用构造都执行,只要创建对象就会执行构造代码块,主要作用是对对象进行初始化 静态代码块:在类中方法外出现,加了static修饰符,最先被执行,且对于一个类的多个对象只执行一次,其主要作用是对类进行初始化,随着类的加载而执行,与创不创建对象无关 同步代码块:在方法中出现,使用synchronized关键字修饰,在多线程环境下,对共享数据的读写操作是需要互斥进行的,否则会导致数据的不一致性以上代码块执行顺序:

1静态代码块-->构造代码块-->构造方法-->局部代码块

继承

继承:多个类中存在相同属性(成员变量)和行为(成员方法)时,将这些内容抽取到单独一个类中,那么多个类就无需再定义这些属性和行为了,只要继承那个类即可通过extends关键字可以实现类与类的继承

class 子类名 extends 父类名{}

单独的类称为父类、基类或超类,子类也叫派生类,父类中的内容是多个子类重复的内容 继承的好处:1、提高了代码的复用性,多个类相同的成员可以放到同一个类中;2、提高了代码的维护性,如果功能的代码需要修改,只需要修改父类这一处即可;3、让类与类之间产生了关系,这是多态的前提(这也是继承的缺点),使得类的耦合性增强 开发的原则:低耦合、高内聚 继承的缺点:1、破坏了封装,子类与父类之间紧密耦合,子类依赖父类的实现,造成子类缺乏独立性;2、支持扩展,但是往往以增强系统结构的复杂度为代价;3、不支持动态继承,在运行时子类无法选择不同的父类;4、子类不能改变父类的接口 继承的特点:1、Java只支持单继承,不支持类的多继承,一个类只能有一个父类,不可以有多个父类;2、Java支持多层继承(继承体系)

class A{}
class B extends A{}
class C extends B{}

子类只能继承父类中所有非私有的成员方法和成员变量 子类不能继承父类的构造方法,但是可以通过super关键字去访问父类的构造方法 不要为了部分功能而去继承 在Java中,所有的继承都是公共继承 继承绝对不会删除任何字段或方法 Object类是Java中所有类的父类,如果某个类没有明确地指出父类,那么Object类就被认为是这个类的父类,这个类就默认访问Object类的无参构造

在子类中访问一个变量的执行顺序:首先在子类的局部范围中找,然后在子类成员范围中找,最后在父类成员范围中找(不能访问到父类的局部范围),如果还是没有就报错 super关键字

super有两个含义:1、调用父类的方法;2、调用父类的构造器
调用父类成员变量

super.成员变量

调用父类的构造方法(使用super调用构造器的语句必须是子类构造器的第一条语句)
super(参数)

调用父类的成员方法
super.成员方法

注:

super不是一个对象的引用,不能将值super赋给另一个对象变量,它只是一个指示编译器调用父类方法的特殊关键字 this代表本类对应的引用,通过其操作本类的成员,super代表父类存储空间的标识

继承中构造方法的关系

子类中所有的构造方法默认都会访问父类中的无参构造方法,因为子类会继承父类中的数据,可能还会使用父类的数据,所以子类初始化之前一定要先完成父类数据的初始化(先进行父类的初始化再进行子类的初始化,这叫分层初始化) 子类每一个构造方法的第一条语句都是super(),这是系统默认的,写不写都有,但如果父类没有无参构造方法,这时在子类中系统就不会自动调用父类的无参构造了,需手动调用父类的构造方法,不然会报错

继承使用场景

采用假设法:如果有两个类A、B,只要它们符合A是B的一种或者B是A的一种(A is B)或(B is A)这样的关系,就可以考虑使用继承 继承的设计技巧

通过父类定义子类时,只需要在子类中指出子类与父类的不同之处即可,将通用的字段和方法(不管是否是抽象的)都放在父类(不管是不是抽象类)中,而更特殊的方法就放在子类中

多态

多态:一个对象变量可以指示多种实际类型的现象(某一个对象在不同时刻表现出来的多种状态) 多态的使用前提:1、要有继承关系;2、要有方法重写;3、要有父类引用指向子类对象 父类名 f = new 子类名(); 多态的好处:1、提高了代码的维护性;2、提高了代码的扩展性 向上转型 fu f = new zi(); 向下转型 zi z = (zi)f;//要求该f必须是能够转换为zi的注:在进行以上两种转型之前应当对两对象之间进行检查,这是编程的好习惯,instanceof操作符:检查对象之间是否能成功地进行转换 例:

/*
	Manager类继承了Employee类
*/
Employee[] staff = new Employee[3];
Manager boss = new Manager();

if(staff[1] instanceof Manager)
{
	boss = (Manager)staff[1];
}

多态中的成员访问特点

成员变量:编译看父类,运行看父类 成员方法:编译看父类,运行看子类(由于只有成员方法存在方法重写,所以它运行看子类) 静态方法:编译看父类,运行看父类(静态和类相关,算不上重写,所以访问的还是父类的) 动态绑定(后期绑定):在运行时能够自动的选择适当的方法,java中的动态绑定是默认行为,动态绑定是多态得以实现的重要因素 静态绑定(前期绑定):在程序执行前已经被绑定,即在编译过程中就已经知道这个方法是哪个类的方法,此时由编译器获取其它连接程序实现。在Java中,final、private、static修饰的方法以及构造函数都是静态绑定的,不需程序运行,不需具体的实例对象就可以知道这个方法的具体内容。

接口

接口:接口是Java 语言中的一种引用类型,它是抽象方法的集合,如果说类的内部封装了成员变量、构造方法和成员方法,那么接口的内部主要就是封装了方法 接口用关键字interface修饰 public interface 接口名{}

接口不是类,它是另外一种引用数据类型 接口不能创建对象,但是可以被实现,一个实现接口的类需要实现接口中所有的抽象方法,否则它就是抽象类 类与接口的关系为实现关系,即类实现接口,该类可以称为接口的实现类或接口的子类,实现用implements关键字修饰 class 类名 implements 接口名{}

接口不能定义构造方法(因为接口的作用主要是为了扩展功能,并不具体存在,没必要初始化)且接口中只有常量(隐式修饰,public static final) 接口不能创建对象,只能通过其实现类来使用 一个接口可以有多个方法且接口中所有的方法必须是抽象方法,默认修饰符public abstract(隐式修饰),如果需要定义具体方法实现,则此时方法需要使用default修饰 接口不是被类继承而是被类实现 一个接口能继承另一个接口且接口支持多继承 public interface 接口1 extends 接口2, 接口3{} 接口中不能含有静态代码块以及静态方法(这里编译器不会报错,只是在实际开发中这样做是没有意义的) 一个类可以实现多个接口 接口中的方法都是公有的 接口是隐式抽象的,当声明一个接口的时候不必使用abstract修饰 接口中每一个方法是隐式抽象的,声明同样不需要abstract关键字 接口的作用是降低耦合度和扩展功能

注:开发中实现接口的类的类名命名格式:接口名+Impi

接口与抽象类的区别:抽象类中定义的是该继承体系的共性功能,而接口中定义的是该继承体系中的扩展功能(特性功能) 当引用类型作形式参数或返回值时: 类:需要的是该类的对象 抽象类:需要的是该抽象类的子类对象(多态) 接口:需要的是该接口的实现类对象(多态)

内部类

内部类:一个类定义在另一个类里面或一个方法里面,这样的类称为内部类,内部类一般包括:成员内部类、局部内部类、匿名内部类、静态内部类 内部类和外部类之间没有继承关系

直接访问内部类成员的语法格式:

外部类名.内部类名 对象名 = new 外部类名().new 内部类名();

成员内部类

成员内部类是最普通的内部类,位于另一个类的内部 成员内部类可以无条件的访问外部类的所有成员变量和成员方法(包括private成员和静态成员) 当成员内部类拥有和外部类同名的成员变量或方法时,默认情况下访问的是内部类的成员(就近原则),若要访问外部类的同名成员,语法格式为: 外部类名.this.成员变量 外部类名.this.成员方法 在外部类如果要访问成员内部类的成员就必须先创建一个内部类的对象,再通过指向这个对象的引用来访问 内部类是依附于外部类存在的,如果要创建内部类的对象,前提是必须存在一个外部类的对象

局部内部类

局部内部类是定义在一个方法或者一个作用域里面的类,它和成员内部类的区别在于局部内部类的作用范围只限于方法内或该作用域内 局部内部类可以无条件的访问外部类的所有成员变量和成员方法(包括private成员和静态成员) 局部内部类就像是方法里面的一个局部变量一样是不能有public、protected、private以及static修饰的

匿名内部类

匿名内部类是没有名字的内部类且它只能使用一次(和匿名对象相似),通常用来简化代码 匿名内部类的实质是对象(可将匿名内部类当作对象使用),并不是类,它是一个类或子类或实现接口类的一个对象,是一个继承了该类或实现了该接口的子类匿名对象 匿名内部类不能有访问修饰符和static 匿名内部类使用条件:必须继承一个父类或实现一个接口 匿名内部类是唯一一种没有构造方法的类,因此它的使用范围非常有限,大部分匿名内部类用于接口回调

注:一般来说,匿名内部类用于继承其他类或实现接口,并不需要增加额外的方法,只是对继承方法的实现或重写,语法格式:

new 类名或者接口名(){重写方法}//可以理解为一个对象

静态内部类

静态内部类是被static修饰的内部类 当外部类被加载的时候,静态内部类是不会被加载的 当使用静态内部类中的成员时,静态内部类才会被加载且仅仅加载一次,不会有线程安全的问题 与静态成员变量不一样的是静态内部类是不能通过外部类对象访问的 静态内部类不能使用外部类的非静态成员变量和方法,只能访问外部类的静态成员

静态内部类的唯一访问语法格式

外部类名.内部类名 对象名 = new 外部类名.内部类名();

抽象类

抽象类:被abstract修饰的类叫抽象类,抽象类没有实体的东西,其无法直接创建对象(不能被实例化),因为调用抽象方法无意义,如果父类为抽象类,那么子类只有重写了父类中的所有方法抽象方法后才可以创建对象,否则该子类还是一个抽象类,因为假如子类没有对抽象类的所有方法重写,那么子类也就会继承了抽象类中的抽象方法,只要有抽象方法的类就一定是抽象类,但抽象类不一定有抽象方法 抽象类的思想:强制子类重写抽象方法 抽象类的作用:降低接口实现类对接口的实现难度,将接口中不需要使用的方法交给抽象类实现,这样接口实现类只需要对要使用的方法进行重写 抽象方法:被abstract修饰的方法叫抽象方法,其没有方法体,且抽象方法必须存在于抽象类中 注:抽象方法是没有方法体,没有方法体不代表空方法体 public abstract void eat(){...}//这叫空方法体,会报错 public abstract void eat();//这才叫无方法体 抽象类不可被实例化 抽象类是有构造器的 抽象类可以没有抽象方法 抽象类的使用场景一般在运用多态时比较适用 abstract不能与这几个关键字共用:private(私有的方法子类是无法继承的,但是abstract又要求子类需要实现抽象方法,这是矛盾的)、final(类、方法被final修饰后不能被继承和重写,矛盾)、static(通过类名访问抽象方法是没有意义的,因为抽象方法没有方法体) 抽象类中的抽象方法是强制要求子类做的事情,非抽象方法是子类继承的事情,提高代码的复用性

包装类

包装类类型:为了对基本数据类型进行更多更方便的操作,Java就针对每一种基本类型数据提供了对应的类类型,即将基本类型转化为对象操作

7
7

Integer类中常用的方法**:

1Stringint
public static int parseInt(String s);//字符串s必须为由数字组成的字符串,字母的不行

2、进制转换(radix的范围:2<= radix <= 36)
public static String toString(int i, int radix);//i表示被转换的数(十进制数),radix表示进制数

包装类都是被final修饰的,因此不能派生它们的子类

自动装箱规范要求boolean、byte、char<= 127

介于-128和127之间的short和int被包装到固定的对象中,不会创建新的对象

如果在一个条件表达式中混合使用Integer和Double类型,Integer值会拆箱,提升为double再装箱为Double

装箱和拆箱是编译器要做的工作而不是虚拟机的,编译器在生成类的字节码时会插入必要的方法调用,虚拟机只是执行这些字节码

byte>Byte
short > Short
 int >  Interger   // Interger in2 = new Interger("123")基本数据类型转化包装类
int i1  = in1.intValue() //包装类转化为基本数据类型
long > Long
float  > Float   //  Float f1 = new  Float("12.3F")
//  Float f2 = new  Float("12.3") // String
double  > Double
boolean  > Boolean // Boolean b3  = newBooleanr("true123")  忽略了大小写,做了优化bollean 默认false  Bollean 默认null
char  > Character
自动装箱  int num2 = =10;
Interger in1 = num2
自动拆箱   int num3 = in1

基本数据类型,包装类 》 String类型,调用valueof(Xxx,xxx)
float f1 = 12.3f;
String str2 = String.valueOf(f1);  //12.3

Java 运算符

  •   算术运算符
    
  •   关系运算符
    
  •   位运算符
    
  •   逻辑运算符
    
  •   赋值运算符
    
  •   其他运算符
    

算术运算符

9
9

关系运算符

10
10

位运算符

11
11

逻辑运算符

12
12

赋值运算符

13
13

​其他运算符

条件运算符(?:)

variable x = (expression) ? value if true : value if false

instanceof 运算符

( Object reference variable ) instanceof  (class/interface type)
String name = "James";
boolean result = name instanceof String; // 由于 name 是 String 类型,所以返回真

运算符优先级14

流程控制语句

循环结构:

while 循环

while( 布尔表达式 ) {
  //循环内容
}
public class Test {
   public static void main(String[] args){
      int x = 10;
 
      do{
         System.out.print("value of x : " + x );
         x++;
         System.out.print("\n");
      }while( x < 20 );
   }
}

for循环

for(初始化; 布尔表达式; 更新) {//代码语句}
public class Test {
   public static void main(String[] args) {
 
      for(int x = 10; x < 20; x = x+1) {
         System.out.print("value of x : " + x );
         System.out.print("\n");
      }
   }
}

增强 for 循环

for(声明语句 : 表达式)
{
   //代码句子
}
public class Test {
   public static void main(String[] args){
      int [] numbers = {10, 20, 30, 40, 50};
 
      for(int x : numbers ){
         System.out.print( x );
         System.out.print(",");
      }
      System.out.print("\n");
      String [] names ={"James", "Larry", "Tom", "Lacy"};
      for( String name : names ) {
         System.out.print( name );
         System.out.print(",");
      }
   }
}

break 关键字

break 主要用在循环语句或者 switch 语句中,用来跳出整个语句块。

break 跳出最里层的循环,并且继续执行该循环下面的语句。


public class Test {
   public static void main(String[] args) {
      int [] numbers = {10, 20, 30, 40, 50};
 
      for(int x : numbers ) {
         // x 等于 30 时跳出循环
         if( x == 30 ) {
            break;
         }
         System.out.print( x );
         System.out.print("\n");
      }
   }
}

以上实例编译运行结果如下:

10
20
continue 关键字

continue 适用于任何循环控制结构中。作用是让程序立刻跳转到下一次循环的迭代。

在 for 循环中,continue 语句使程序立即跳转到更新语句。

在 while 或者 do…while 循环中,程序立即跳转到布尔表达式的判断语句。


public class Test {
   public static void main(String[] args) {
      int [] numbers = {10, 20, 30, 40, 50};
 
      for(int x : numbers ) {
         if( x == 30 ) {
        continue;
         }
         System.out.print( x );
         System.out.print("\n");
      }
   }
}

以上实例编译运行结果如下:

10
20
40
50

条件语句:

if...else


if(布尔表达式){
   //如果布尔表达式的值为true
}else{
   //如果布尔表达式的值为false
}

ublic class Test {
 
   public static void main(String args[]){
      int x = 30; 
      if( x < 20 ){
         System.out.print("这是 if 语句");
      }else{
         System.out.print("这是 else 语句");
      }
   }
}

if...else if...else 语句

if(布尔表达式 1){
   //如果布尔表达式 1的值为true执行代码
}else if(布尔表达式 2){
   //如果布尔表达式 2的值为true执行代码
}else if(布尔表达式 3){
   //如果布尔表达式 3的值为true执行代码
}else {
   //如果以上布尔表达式都不为true执行代码
}

switch case


switch(expression){
    case value :
       //语句
       break; //可选
    case value :
       //语句
       break; //可选
    //你可以有任意数量的case语句
    default : //可选
       //语句
}

switch case 语句有如下规则:

  • switch 语句中的变量类型可以是: byte、short、int 或者 char。从 Java SE 7 开始,switch 支持字符串 String 类型了,同时 case 标签必须为字符串常量或字面量。

  • switch 语句可以拥有多个 case 语句。每个 case 后面跟一个要比较的值和冒号。

  • case 语句中的值的数据类型必须与变量的数据类型相同,而且只能是常量或者字面常量。

  • 当变量的值与 case 语句的值相等时,那么 case 语句之后的语句开始执行,直到 break 语句出现才会跳出 switch 语句。

  • 当遇到 break 语句时,switch 语句终止。程序跳转到 switch 语句后面的语句执行。case 语句不必须要包含 break 语句。如果没有 break 语句出现,程序会继续执行下一条 case 语句,直到出现 break 语句。

  • switch 语句可以包含一个 default 分支,该分支一般是 switch 语句的最后一个分支(可以在任何位置,但建议在最后一个)。default 在没有 case 语句的值和变量值相等的时候执行。default 分支不需要 break 语句。

    
    public class Test {
       public static void main(String args[]){
          //char grade = args[0].charAt(0);
          char grade = 'C';
     
          switch(grade)
          {
             case 'A' :
                System.out.println("优秀"); 
                break;
             case 'B' :
             case 'C' :
                System.out.println("良好");
                break;
             case 'D' :
                System.out.println("及格");
                break;
             case 'F' :
                System.out.println("你需要再努力努力");
                break;
             default :
                System.out.println("未知等级");
          }
          System.out.println("你的等级是 " + grade);
       }
    }
    
    

    以上代码编译运行结果如下

    良好
    你的等级是 C
    

    如果 case 语句块中没有 break 语句时,JVM 并不会顺序输出每一个 case 对应的返回值,而是继续匹配,匹配不成功则返回默认 case。

    
    public class Test {
       public static void main(String args[]){
          int i = 5;
          switch(i){
             case 0:
                System.out.println("0");
             case 1:
                System.out.println("1");
             case 2:
                System.out.println("2");
             default:
                System.out.println("default");
          }
       }
    }
    
    

    以上代码编译运行结果如下:

    default
    

    如果 case 语句块中没有 break 语句时,匹配成功后,从当前 case 开始,后续所有 case 的值都会输出。

    public class Test {
       public static void main(String args[]){
          int i = 1;
          switch(i){
             case 0:
                System.out.println("0");
             case 1:
                System.out.println("1");
             case 2:
                System.out.println("2");
             default:
                System.out.println("default");
          }
       }
    }
    
    

    以上代码编译运行结果如下:

    1
    2
    default
    

Java 枚举(enum)

当需要定义一组常量时,强烈建议使用枚举类

JDK1.5之前需要自定义枚举类

JDK 1.5 新增的 enum关键字用于定义枚举类

枚举类的属性

枚举类对象的属性不应允许被改动,所以应该使用 private final修饰

枚举类的使用 private final 修饰的属性应该在构造器中为其赋值

若枚举类显式的定义了带参数的构造器, 则在列出枚举值时也必须对应的 传入参数

自定义枚举类

15
15

使用enum 定义枚举类

16
16

Enum 类的 主要 方法

17
17

Enum类 类 的主要方法

values() 方法:返回枚举类型的对象数组。该方法可以很方便地遍历所有的枚举值

valueOf(String str):可以把一个字符串转为对应的枚举类对象。要求字符串必须是枚举类对象的“名字”。如不是,会有运行时异常:IllegalArgumentException

toString()返回当前枚举类对象常量的名称

枚举类可以实现一个或多个接口

若每个枚举值在调用实现的接口方法呈现相同的行为方式,则只要统一实现该方法

若需要每个枚举值在调用实现的接口方法呈现出不同的行为方式,则可以让每个枚举值分别来实现该方法

泛型

泛型是一种把数据类型明确的工作推迟到创建对象或者调用方法的时候才去明确的特殊的类型,也叫参数化类型,把类型当作参数一样传递

泛型的格式

<数据类型> 注:这里的数据类型只能是引用类型

例:ArrayList<String> array = new ArrayList<String>();//array这个集合中所有元素都是String类型的,且只能是String类型的

泛型的好处:1、把运行时期的问题提前到了编译期间;2、避免了强制类型转换;3、优化了程序设计;4、增强了程序的安全性

看API,如果类、接口、抽象类后面跟有< E >就表示要使用泛型,一般来说在集合中使用的情况居多

泛型通配符

  • < ? >:任意类型,如果没有明确,那么就是Object以及任意的Java类了
  • 泛型如果明确时前后必须一致
  • ?表示任意的类型都可以(后面new的类型是任意引用类型)
Collection<?> c1 = new ArrayList<Object>();
Collection<?> c2 = new ArrayList<Animal>();
Collection<?> c3 = new ArrayList<Dog>();
Collection<? extends Animal> c = new ArrayList<Dog>();//Dog继承类Animal

IO流

按照流的方向进行分类(以内存作为参照物):

输入流:数据从硬盘到内存里去叫作输入或读,可理解为读进去

输出流:数据从内存中出来到硬盘叫作输出或写,可理解为写出来

按照数据的读取方式进行分类:

字节流:按照字节的方式读取数据,一次读取一个字节byte,等同于一次读取8个二进制位,这种流是万能的,什么类型的文件都可以读取,包括:文本文件、图片、声音文件、视频文件

字符流:按照字符的方式读取数据,一次读取一个字符,这种流是为了方便读取普通文本文件而存在的,但这种流不能读取:图片、声音、视频等文件,只能读取纯文本文件,也读不了word文件

总:在Java中只要类名以“Stream”结尾的都是字节流,以“Reader/Writer”结尾的都是字符流

字节输入流:InputStream

字节输出流:OutputStream

字符输入流:Reader

字符输出流:Write

注:1、以上所有的流都实现了Closeable接口,表明所有的流都可以关闭,都有close()方法,切记用完流之后一定要关闭流。2、所有的输出流都实现了Flushable接口,表明所有的输出流都是可以刷新的,都有flush()方法,输出流在最终输出完后一定要记得flush()方法刷新一下,这个刷新表示将通道当中剩余未输出的数据强行输出完(清空通道),如果没有flush()方法可能为导致数据丢

文件专属流

字节输入流(FileInputStream)

常用方法

1、返回流当中剩余的没有读到的字节数量
public int available();

2、跳过n个字节不读
public long skip(long n);

3、返回读到的字节值,每次读一个字节,读完自动后移,文件读完返回-1
public int read();

4、将读到的内容存入字节数组中,且最多读入b.length个字节,返回读到的字节总数,文件读完返回-1
public int read(byte[] b);

字节输出流(FileOutputStream)

常用方法

1、将指定 byte 数组中从偏移量 off 开始的 len 个字节写入此文件输出流
public void write(byte[] b, int off, int len);


字符输出流与字符输入流的常用方法与以上的区别在于把byte数组换成了char数组

用字节流完成文本复制
FileInputStream fis = null;
		FileOutputStream fos = null;
		try{
			//创建字节输入流对象
			fis = new FileInputStream("src/lianxi_io/myio_1");
			//创建字节输出流对象,true表示追加
			fos = new FileOutputStream("src/lianxi_io/myio_2", true);
			
			byte[] bytes = new byte[1024];
			int rcount = 0;
			while((rcount = fis.read(bytes)) != -1)
			{
				fos.write(bytes, 0, rcount);
			}
			//输出流刷新(刷新缓冲区)
			fos.flush();
		} catch (FileNotFoundException e){
			
			e.printStackTrace();
		} catch (IOException e){
			e.printStackTrace();
		} finally {
			//关闭流
			if(fis != null)
			{
				try{
					fis.close();
				} catch (IOException e){
					e.printStackTrace();
				}
			}
			
			if(fos != null)
			{
				try{
					fos.close();
				} catch (IOException e){
					e.printStackTrace();
				}
			}
		}

转换流

InputStreamReader

通过构造方法将字节输入流转换为字符输入流
public InputStreamReader(InputStream in);

OutputStreamWriter

通过构造方法将字节输出流转换为字符输出流
public OutputStreamWriter(OutputStream out);

:当一个流的构造方法中需要一个流的时候,这个被传进来的流叫作节点流,外部负责包装的这个流叫包装流或处理流,只需关闭包装流就行,当包装流关闭后节点流会自动关闭

缓冲流

缓冲流是为了提高数据的读写速度而存在的,因为缓冲流自带缓冲区,不需要再定义数组

  • BufferedReader
  • BufferedWriter
  • BufferedInputStream
  • BufferedOutputStream

注:其中BufferedReader的特殊方法

一次读取一个文本行,包含该行内容的字符串,不包含任何行终止符,如果已到达文本末尾,则返回 null 
public String readLine();

数据流

数据流中DateOutputStream可以将数据连同数据的类型一并写入文件中,这个文件不是普通的文本文件,用记事本打不开,只能使用DateInputStream去读,并且读的时候需要与写的顺序一致才可以正常取出数据

标准输出流

  • printStream

  • printWriter

    标准的输出流默认输出到控制台

System.out.println("Hello");

改变输出方向,输入到指定文本中

PrintStream ps = new PrintStream(new FileOutputStream("src/lianxi_io/myio_1", true));
//改变输出方向
System.setOut(ps);

序列化与反序列化

序列化:Java对象按照流的方式存入文本文件或网络中,将Java对象的状态保存下来的过程,通过ObjectOutputStream完成(对象---->流数据)

反序列化:将硬盘上的流数据重新恢复到内存中,恢复成Java对象,通过ObjectInputStream完成(流数据---->对象)

参与序列化和反序列化的对象必须实现serializable接口

erializable接口只是一个标志接口,这个接口当中什么也没有,它是给JVM参考的,当JVM识别到这个接口后,就会为实现该接口的类自动生成一个序列化版本号用来标识类 Java虚拟机识别一个类的时候先通过类名,如果类名一致就再通过序列化版本号 凡是一个类实现了serializable接口,建议手动给该类提供一个固定不变的序列化版本号,这样,以后这个类即使被修改了但是版本号没有变,Java虚拟机也会认为是同一类

private static final long serialVersionUID= 1L;

在实现serializable接口的类中若不想某个成员变量被序列化,可用transient关键字声明

实例:

序列化
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectOutputStream;
import java.io.Serializable;

public class IoTest
{
	public static void main(String[] args)
	{
		ObjectOutputStream oos = null;
		try{
			//序列化
			oos = new ObjectOutputStream(new FileOutputStream("src/lianxi_io/myio_1"));
			
			oos.writeObject(new Student("xss", 23));
			oos.writeObject(new Student("xxx", 34));
			
			oos.flush();
		} catch (FileNotFoundException e){
			e.printStackTrace();
		} catch (IOException e){
			e.printStackTrace();
		} finally {
			try{
				oos.close();
			} catch (IOException e){
				e.printStackTrace();
			}
		}
	}
}

class Student implements Serializable
{
	//手动固定序列化版本号
	private static final long serialVersionUID = 2L;
	private String name;
	private int age;
	private String address;

	public Student()
	{

	}

	public Student(String name, int age)
	{
		super();
		this.name = name;
		this.age = age;
	}

	public String toString()
	{
		return "Student [name=" + name + ", age=" + age + "]";
	}

}

反序列化
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.ObjectInputStream;

public class lianxi
{
	public static void main(String[] args)
	{
		ObjectInputStream ois = null;
		try{
			//反序列化
			ois = new ObjectInputStream(new FileInputStream("src/lianxi_io/myio_1"));
			
			Object obj1 = ois.readObject();
			System.out.println(obj1);
			Object obj2 = ois.readObject();
			System.out.println(obj2);
		} catch (FileNotFoundException e){
			e.printStackTrace();
		} catch (IOException e){
			e.printStackTrace();
		} catch (ClassNotFoundException e){
			e.printStackTrace();
		} finally {
			try{
				ois.close();
			} catch (IOException e){
				e.printStackTrace();
			}
		}
	}

}


File类

相对路径:从当前路径开始(在IDEA和Eclipse中,默认的当前路径是项目(project)路径)

绝对路径:从盘符开始

File类的概述:文件和目录路径的抽象表示形式

File类的构造方法

1、根据一个路径得到FIle对象
public File(String pathname);

2、根据一个目录和一个子文件/目录得到File对象
public File(String parent, String child);

3、根据一个父File对象和一个子文件/目录得到对象
public File(File parent, String child);

创建功能

1、创建文件,如果已存在该文件就不再创建并返回false
public boolean createNewFile();

2、创建文件夹,如果已存在该文件就不再创建并返回false
public boolean mkdir();

3、创建文件夹,且是多级创建,如果父文件夹不存在就创建
public boolean mkdirs()

删除功能(Java中的删除不走回收站)

删除文件或文件夹
public boolean delete();

重命名功能(如果路径相同就是重命名,路径不同就是重命名加剪切)

public boolean renameTo(File dest);

判断功能

1、判断是否是目录/文件夹
public boolean isDirectory()

2、判断是否是文件
public boolean isFile();

3、判断是否存在
public boolean exists();

4、判断是否可读
public boolean canRead();

5、判断是否可写
public boolean canWrite();

6、判断是否隐藏
public boolean isHidden();

基本获取功能


1、获取绝对路径
public String getAbsolutePath();

2、获取相对路径
public String getPath();

3、获取名称
public String getName();

4、获取文件大小
public long length();

5、获取最后一次修改时间,毫秒值
public long lastModified();

高级获取功能

1、获取指定目录下的所有文件或文件夹的名称数组
public String[] list();

2、获取指定目录下的所有文件或文件夹的File数组
public File[] listFiles();

Java 异常

运行时异常

是指编译器不要求强制处置的异常。一般是指编程时的逻辑错误,是程序

员应该积极避免其出现的异常。java.lang.RuntimeException类及它的子

类都是运行时异常。

对于这类异常,可以不作处理,因为这类异常很普遍,若全处理可能会对

程序的可读性和运行效率产生影响。

编译时异常

是指编译器要求必须处置的异常。即程序在运行时由于外界因素造成的一

般性异常。编译器要求Java程序必须捕获或声明所有编译时异常。

对于这类异常,如果程序不处理,可能会带来意想不到的结果。

方式一:try-catch-finally

异常处理是通过try-catch-finally语句实现的。
try{
...... //可能产生异常的代码
}
catch( ExceptionName1 e ){
...... //当产生ExceptionName1型异常时的处置措施
}
catch( ExceptionName2 e ){
...... //当产生ExceptionName2型异常时的处置措施
}
[ finally{
...... //无论是否发生异常,都无条件执行的语句
} ]
getMessage() 获取异常信息,返回字符串
printStackTrace() 获取异常类名和异常信息,以及异常出
现在程序中的位置。返回值void。

方式二:throws + 异常类型

如果一个方法(中的语句执行时)可能生成某种异常,但是并不能确定如何处理这

种异常,则此方法应显示地声明抛出异常,表明该方法将不对这些异常进行处理,

而由该方法的调用者负责处理。

在方法声明中用throws语句可以声明抛出异常的列表,throws后面的异常类型可

以是方法中产生的异常类型,也可以是它的父类。

public void readFile(String file) throws FileNotFoundException {
……
// 读文件的操作可能产生FileNotFoundException类型的异常
FileInputStream fis = new FileInputStream(file);
..……
}

重写方法不能抛出比被重写方法范围更大的异常类型。在多态的情况下,

对methodA()方法的调用-异常的捕获按父类声明的异常处理。

public class A {
public void methodA() throws IOException {
……
} }
public class B1 extends A {
public void methodA() throws FileNotFoundException {
……
} }
public class B2 extends A {
public void methodA() throws Exception { //报错
……
} }

手动抛出异常

Java异常类对象除在程序执行过程中出现异常时由系统自动生成并 抛出,也可根据需要使用人工创建并抛出。

首先要生成异常类对象,然后通过throw语句实现抛出操作(提交给Java运行环境)。

IOException e = new IOException();
throw e;

用户自定义异常类

一般地,用户自定义异常类都是RuntimeException的子类。

自定义异常类通常需要编写几个重载的构造器。

自定义异常需要提供serialVersionUID

自定义的异常通过throw抛出。

自定义异常最重要的是异常类的名字,当异常出现时,可以根据 名字判断异常类型

class MyException extends Exception {
static final long serialVersionUID = 13465653435L;
private int idnumber;
public MyException(String message, int id) {
super(message);
this.idnumber = id;
}
public int getId() {
return idnumber;
}
}
public class MyExpTest {
public void regist(int num) throws MyException {
if (num < 0)
throw new MyException("人数为负值,不合理", 3);
else
System.out.println("登记人数" + num);
}
public void manager() {
try {
regist(100);
} catch (MyException e) {
System.out.print("登记失败,出错种类" + e.getId());
}
System.out.print("本次登记操作结束");
}
public static void main(String args[]) {
MyExpTest t = new MyExpTest();
t.manager();
}
}

Java 泛型

public class DAO {
public <E> E get(int id, E e) {
E result = null;
return result;
}
}
public static <T> void fromArrayToCollection(T[] a, Collection<T> c) {
for (T o : a) {
c.add(o);
}
}
public static void main(String[] args) {
Object[] ao = new Object[100];
Collection<Object> co = new ArrayList<Object>();
fromArrayToCollection(ao, co);
String[] sa = new String[20];
Collection<String> cs = new ArrayList<>();
fromArrayToCollection(sa, cs);
Collection<Double> cd = new ArrayList<>();
// 下面代码中T是Double类,但sa是String类型,编译错误。
// fromArrayToCollection(sa, cd);
// 下面代码中T是Object类型,sa是String类型,可以赋值成功。
fromArrayToCollection(sa, co);
}
class Creature{}
class Person extends Creature{}
class Man extends Person{}
class PersonTest {
public static <T extends Person> void test(T t){
System.out.println(t);
}
public static void main(String[] args) {
test(new Person());
test(new Man());
//The method test(T) in the type PersonTest is not 
//applicable for the arguments (Creature)
test(new Creature());
}
}

继承性

public void testGenericAndSubClass() {
Person[] persons = null;
Man[] mans = null;
// 而 Person[] 是 Man[] 的父类.
persons = mans;
Person p = mans[0];
// 在泛型的集合上
List<Person> personList = null;
List<Man> manList = null;
// personList = manList;(报错)
}

通配符的使用

1.使用类型通配符:?
比如:List<?> ,Map<?,?>
List<?>是List<String>、List<Object>等各种泛型List的父类。
2.读取List<?>的对象list中的元素时,永远是安全的,不管list的真实类型是什么,它包含的都是Object。
3.写入list中的元素时,不行。因为我们不知道c的元素类型,我们不能向其中添加对象。
唯一的例外是null,它是所有类型的成员。
将任意元素加入到其中不是类型安全的:
Collection<?> c = new ArrayList<String>();
c.add(new Object()); // 编译时错误
因为我们不知道c的元素类型,我们不能向其中添加对象。add方法有类型参数E作为集
合的元素类型。我们传给add的任何参数都必须是一个未知类型的子类。因为我们不知
道那是什么类型,所以我们无法传任何东西进去。
唯一的例外的是null,它是所有类型的成员。
另一方面,我们可以调用get()方法并使用其返回值。返回值是一个未知的类型,但是我们知道,它总是一个Object
public static void main(String[] args) {
List<?> list = null;
list = new ArrayList<String>();
list = new ArrayList<Double>();
// list.add(3);//编译不通过
list.add(null);
List<String> l1 = new ArrayList<String>();
List<Integer> l2 = new ArrayList<Integer>();
l1.add("你好");
l2.add(15);
read(l1);
read(l2);
}
public static void read(List<?> list) {
for (Object o : list) {
System.out.println(o);
}
}
<? extends Number> (无穷小 , Number]只允许泛型为Number及Number子类的引用调用
<? super Number> [Number , 无穷大)只允许泛型为Number及Number父类的引用调用
<? extends Comparable> 只允许泛型为实现Comparable接口的实现类的引用调用

Java 注解

Java 注解是附加在代码中的一些元信息,用于一些工具在编译、运行时进行解析和使用,起到说明、配置的功能。注解不会也不能影响代码的实际逻辑,仅仅起到辅助性的作用。

框架 = 注解 + 反射 + 设计模式

注解定义语法

[修饰符列表] @interface 注解类型名{
	
}

注解使用语法

@ 注解类型名

@Override这个注解只能注解方法,这个注解是给编译器参考的,和运行阶段没有关系,凡是java中的方法带有这个注解的,编译器都会进行编译检查,如果这个方法不是重写父类的方法,编译器就会报错

用来标注“注解类型”的注解称为元注解

@Target注解是一个元注解,用来标注“被标注的注解”可以出现在哪些位置上

v@Target(ElementType.METHOD)//被标注的元素只能出现在方法上
@Target(ElementType.ANNOTATION_TYPE)//被标注的元素只能出现在注解类型上
@Target(ElementType.TYPE)//被标注的元素只能出现在类上
@Target(ElementType.FIELD)//被标注的元素只能出现在字段上
@Target(ElementType.PARAMETER)//被标注的元素只能出现在参数上
@Target(ElementType.CONSTRUCTOR)//被标注的元素只能出现在构造方法上
@Target(ElementType.LOCAL_VARIABLE)//被标注的元素只能出现在局部变量上
@Target(ElementType.PACKAGE)//被标注的元素只能出现在包上
@Target(ElementType. MODULE)//被标注的元素只能出现在模块上

@Retention注解是一个元注解,用来标注“被标注的注解”最终保存在哪里

@Retention(RetentionPolicy.SOURCE)//被标注的元素只被保存在java源文件中
@Retention(RetentionPolicy.CLASS)//被标注的元素只被保存在class文件中
@Retention(RetentionPolicy.RUNTIME)//被标注的元素只被保存在class文件中,并且可以被反射机制所读取

@Deprecated注解用来标注已过时的元素

注解中定义属性

属性类型 属性名();//表示该属性名只能被赋值该属性类型的数据

public @interface MyAnnotation
{
	int value();
	String name();
	String address() default "";//给address属性赋默认值,在该注解使用时可省略不写
}

如果一个注解当中有属性,那么在使用该注解时必须给该注解中的属性赋值(除非该属性使用default指定默认值就可省略赋值)

如果一个注解的属性名为value且只有这一个属性或其余属性有默认值时,那么在使用该注解的这个属性时,属性名(value)可以省略不写,但属性值必须要有

注解使用

@ 注解类型名(属性名=属性值, 属性名=属性值......)

@MyAnnotation(value = 9, name = "xxx", address = "dddd")
class Person
{
	@MyAnnotation(value = 9, name = "xxx" )//因属性address有默认值可省略
	public void run()
	{
		
	}
}
  • 注解当中的属性可以是这些类型:byte、short、int、long、float、double、boolean、char、String、class、枚举以及以上每种类型的数组类型
  • 在使用注解中数组型的属性时如果属性值只有一个值,大括号{ }可省略不写

反射注解

判断这个注解是否在此类上
//括号里填注解类型的字节码,
public boolean isAnnotationPresent(Class<? extends Annotation> annotationClass);//返回true表示在此类上有此注解,false表示没有

获取该注解对象
public <A extends Annotation> A getAnnotation(Class<A> annotationClass);//返回该注解类型,可能需强转

Java 集合

Collection

18
18

数组和集合类同是容器,区别在于:

1、数组虽然也可以存储对象(对象数组),但长度是固定的,集合长度是可变的;

2、数组可存储基本数据类型,但集合只能存储对象的内存地址(引用),且集合可存储不同类型的对象

**Collection接口:List元素有序、可重复的集合,Set元素无序、不可重复的集合

集合继承体系图

ArrayList:底层数据结构是动态数组,默认初始化容量为10(底层先创建了一个长度为0的数组,当添加第一个元素的时候,初始化容量为10),自动扩容(扩容到原容量的1.5倍),支持随机访问,查询快,增删慢,非同步,线程不安全,效率高 Vector:底层数据结构是动态数组,查询快,增删慢,同步,线程安全,效率低 LinkedList:底层数据结构是双向链表,不支持随机访问,查询慢,增删快,非同步,线程不安全,效率高

Collection中的常用功能

1、添加功能
boolean add(Object obj);//添加一个元素
boolean addAll(Collection c);//添加一个集合的元素

2、删除功能
void clear();//移除所有元素
boolean remove(Object o);//移除一个元素
boolean removeAll(Collection c);//移除一个集合的元素,只要有一个元素被移除就返回true

3、判断功能
boolean contains(Object o);//判断集合中是否包含指定元素
boolean containsAll(Collection c);//判断集合中是否包含指定的集合元素,只有包含所有元素才叫包含,才返回true
boolean isEmpty();//判断集合是否为空

4、长度功能
int size();//这里获取的是元素的实际个数,不是集合的容量

5、交集功能
/*
	设有两个集合A、B
	A.retainAll(B);
	若A、B之间有交集就把交集保存到集合A中,若无交集,那么集合A就为空,
	至于返回结果则取决于保存交集的A集与保存之前的集合内容是否有变化,
	有变化就返回true,没有变化就返回false
*/
boolean retainAll(Collection c);

6、集合转数组
Object[] toArray();//转型后的数组中每一个元素都是Object类型的
7、获取集合对象的哈希值
    hashCode()
8、遍历
    iterator():返回迭代器对象,用于集合遍历

迭代器(集合的专用遍历方式)

Iterator<E> iterator();

Iterator接口中的方法:

Object next();//获取元素之后自动后移
boolean hasNext();//判断是否还有元素,如果仍有元素可以迭代就返回true

迭代举例

Collection c = new ArrayList();
方式一:
Iterator it = c.iterator();//通过集合对象获取迭代器对象
while(it.hasNext())
{
	System.out.println(it.next());
}

方式二:
for(Iterator it = c.iterator(); it.hasNext();)
{
	System.out.println(it.next());
}


20
20
21
21
22
22

List集合:

有序的Collection(也称为序列),此接口的用户可以对列表中每个元素的插入位置进行精确控制,用户可以根据元素的整数索引(在列表中的位置)访问元素,并搜索列表中的元素,与Set不同的是List允许重复的元素 List集合的特点:1、有序(存储和取出的元素顺序一致);2、可重复 List集合特有的遍历

List  c = new ArrayList();
for(int i = 0; i < c.size(); i++)
{
	System.out.println(c.get(i));
}

List集合特有的迭代器(列表迭代器)

列表迭代器:ListIterator listIterator();
该迭代器继承了Iterator迭代器,所以也可直接使用hasNext()next()

列表迭代器特有的功能:
逆向遍历:
	Object previous();//获取上一个元素
	boolean hasprevious();//判断是否有元素
正向遍历:
	Object next();
	boolean hasNext();

注:ListIterator可以实现逆向遍历,但是必须同一迭代器正向遍历之后才能进行逆向遍历,无实际意义,一般不使用

List除了从Collection集合继承的方法外,List 集合里添加了一些根据索引来 操作集合元素的方法。

void add(int index, Object ele): 在index 位置插入ele 元素
boolean addAll(int index, Collection eles): 从index 位置开始将eles中 中的所有元素添加进来

Object get(int index): 获取指定index 位置的元素
int indexOf(Object obj): 返回obj 在集合中首次出现的位置
int lastIndexOf(Object obj): 返回obj 在当前集合中末次出现的位置

Object remove(int index): 移除指定index 位置的元素,并返回此元素
Object set(int index, Object ele): 设置指定index 位置的元素为ele
List subList(int fromIndex, int toIndex): 返回从fromIndex 到toIndex位置的子集合

LinkedList集合特有的功能

LinkedList: 双向链表,内部没有声明数组,而是定义了Node类型的first和last, 用于记录首末元素。同时,定义内部类Node,作为LinkedList中保存数据的基 本结构。Node除了保存数据,还定义了两个变量: prev变量记录前一个元素的位置 next变量记录下一个元素的位置

private static class Node<E> {
E item;
Node<E> next;
Node<E> prev;
Node(Node<E> prev, E element, Node<E> next) {
this.item = element;
this.next = next;
this.prev = prev;
}
}
1、添加功能
public void addFirst(Object o);//开头添加
public void addLast(Object o);//结尾添加

2、获取功能
public Object getFirst();//获取开头元素
public Object getLast();//获取结尾元素

3、删除功能
public Object removeFirst();//删除开头元素并返回被删除元素
public Object removeLast();//删除结尾元素并返回被删除元素

ArrayList和LinkedList的异同

二者都线程不安全,相对线程安全的Vector,执行效率高。 此外,ArrayList是实现了基于动态数组的数据结构,LinkedList基于链表的数据结构。对于 随机访问get和set,ArrayList觉得优于LinkedList,因为LinkedList要移动指针。对于新增 和删除操作add(特指插入)和remove,LinkedList比较占优势,因为ArrayList要移动数据

ArrayList和Vector的区别

Vector和ArrayList几乎是完全相同的,唯一的区别在于Vector是同步类(synchronized),属于 强同步类。因此开销就比ArrayList要大,访问要慢。正常情况下,大多数的Java程序员使用 ArrayList而不是Vector,因为同步完全可以由程序员自己来控制。Vector每次扩容请求其大 小的2倍空间,而ArrayList是1.5倍。Vector还有一个子类Stack。

Set 接口

HashSet

是 Set 接口的典型实现,大多数时候使用 Set 集合时都使用这个实现类

HashSet 按 Hash 算法来存储集合中的元素,因此具有很好的存取、查找、删除性能

HashSet 特点
不能保证元素的排列顺序,HashSet 不是线程安全的,集合元素可以是 null

HashSet 集合判断两个元素相等的标准:两个对象通过 hashCode() 方法比较相
等,并且两个对象的 equals() 方法返回值也相等。

对于存放在Set容器中的对象, 对应的类一定要重写equals() 和hashCode(Object
obj) 方法,以实现对象相等规则 。即: “相等的对象必须具有相等的散列码” 。
向HashSet中添加元素的过程:
当向 HashSet 集合中存入一个元素时,HashSet 会调用该对象的 hashCode() 方法
来得到该对象的 hashCode 值,然后根据 hashCode 值,通过某种散列函数决定该对象
在 HashSet 底层数组中的存储位置。(这个散列函数会与底层数组的长度相计算得到在
数组中的下标,并且这种散列函数计算还尽可能保证能均匀存储元素,越是散列分布,
该散列函数设计的越好)

如果两个元素的hashCode()值相等,会再继续调用equals方法,如果equals方法结果
为true,添加失败;如果为false,那么会保存该元素,但是该数组的位置已经有元素了,
那么会通过链表的方式继续链接。

如果两个元素的 equals() 方法返回 true,但它们的 hashCode() 返回值不相
等,hashSet 将会把它们存储在不同的位置,但依然可以添加成功。

底层也是数组,初始容量为16,当如果使用率超过0.75,(16*0.75=12)
就会扩大容量为原来的2倍。(16扩容为32,依次为64,128....等)
重写 hashCode() 方法的基本原则
在程序运行时,同一个对象多次调用 hashCode() 方法应该返回相同的值

当两个对象的 equals() 方法比较返回 true 时,这两个对象的 hashCode()方法的返回值也应相等。

对象中用作 equals() 方法比较的 Field,都应该用来计算 hashCode 值。

###### 重写 equals() 方法的基本原则

 当一个类有自己特有的“逻辑相等”概念,当改写equals()的时候,总是要改写hashCode(),根据一个类的equals方法(改写后),两个截然不同的实例有可能在逻辑上是相等的,但是,根据Object.hashCode()方法,它们仅仅是两个对象。复写equals方法的时候一般都需要同时复写hashCode方法。通 通算 常参与计算hashCode 的对象的属性也应该参与到equals() 中进行计算

LinkedHashSet

LinkedHashSet 是 HashSet 的子类,LinkedHashSet 根据元素的 hashCode 值来决定元素的存储位置,
但它同时使用双向链表维护元素的次序,这使得元素看起来是以插入顺序保存的。

LinkedHashSet插入性能略低于 HashSet,但在迭代访问 Set 里的全部元素时有很好的性能

LinkedHashSet 不允许集合元素重复。

TreeSet

TreeSet 是 SortedSet 接口的实现类,TreeSet 可以确保集合元素处于排序状态。

TreeSet底层使用 红黑树结构存储数据,

特点:有序,查询速度比List快

Comparator comparator()
Object first()
Object last()
Object lower(Object e)
Object higher(Object e)
SortedSet subSet(fromElement, toElement)
SortedSet headSet(toElement)
SortedSet tailSet(fromElement)
TreeSet 两种排序方法:

自然排序和 定制排序, 。默认情况下,TreeSet 采用自然排序。

排 序— 自然排序

自然排序:TreeSet 会调用集合元素的 compareTo(Object obj) 方法来比较元素之间的大小关系,然后将集合元素按升序(默认情况)排列 如果试图把一个对象添加到 TreeSet 时,则该对象的类必须实现 Comparable接口。 实现 Comparable 的类必须实现 compareTo(Object obj) 方法,两个对象即通过compareTo(Object obj) 方法的返回值来比较大小。 Comparable 的典型实现:

BigDecimal、BigInteger 以及所有的数值型对应的包装类:按它们对应的数值大小进行比较 Character:按字符的 unicode值来进行比较 Boolean:true 对应的包装类实例大于 false 对应的包装类实例 String:按字符串中字符的 unicode 值进行比较 Date、Time:后边的时间、日期比前面的时间、日期大

向 TreeSet 中添加元素时,只有第一个元素无须比较compareTo()方法,后面添加的所有元素都会调用compareTo()方法进行比较。

只有相同类的两个实例才会比较大小,所以向 TreeSet 中添加的应该是同一个类 的 对象。

对于 TreeSet 集合而言,它判断两个对象是否相等的唯一标准是:两个对象通过 compareTo(Object obj) 方法比较返回值。

当需要把一个对象放入 TreeSet 中,重写该对象对应的 equals() 方法时,应保证该方法与 compareTo(Object obj) 方法有一致的结果:如果两个对象通过equals() 方法比较返回 true,则通过 compareTo(Object obj) 方法比较应返回 0。

排 序— 定制 排序

TreeSet的自然排序要求元素所属的类实现Comparable接口,如果元素所属的类没有实现Comparable接口,或不希望按照升序(默认情况)的方式排列元素或希望按照 其它属性大小进行排序,则考虑使用定制排序。定制排序,通过Comparator接口来实现。需要重写compare(T o1,T o2)方法。利用int compare(T o1,T o2)方法,比较o1和o2的大小:如果方法返回正整数,则表示o1大于o2;如果返回0,表示相等;返回负整数,表示o1小于o2。要实现定制排序,需要将实现Comparator接口的实例作为形参传递给TreeSet的构造器。只能向TreeSet中添加类型相同的对象。否则发生ClassCastException异常。

使用定制排序判断两个元素相等的标准是:通过Comparator比较两个元素返回了0。

23
23

Map

19
19

Map集合:Map集合中键和值的关系——映射,可以通过键来获取值 Map集合的特点:将键映射到值的对象,一个映射不能包含重复的键,每个键最多只能映射到一个值(无序不重复) Map集合与Collection集合的区别:1、Map集合存储元素是成对出现的(键值对),Map集合的键是唯一的,值是重复的,Collection集合存储元素是单独出现的,Collection的Set是唯一的,List是可重复的;2、Map集合的数据结构针对键有效,跟值无关,Collecton集合的数据结构针对元素有效 给定一个键和一个值就可以将该值存储在一个Map对象中,之后可通过键来访问对应的值当访问的值不存在的时候,方法就会抛出一个NoSuchElementException异常 当对象的类型和Map里元素类型不兼容时,方法就会抛出一个ClassCastException异常,当在不允许使用Null对象的Map中使用Null对象时,方法就会抛出一个NullPointerException异常 Map集合中常用的功能:

1、添加功能
/*
(key表示键,value表示值)如果键是第一次存储就直接存储元素并返回null,
如果键不是第一次存储就用现在的值把以前的值替换掉并返回以前的值
*/
v put(k key, v value);

2、删除功能
void clear();//移除所有的键值对元素
v remove(Object key);//根据键删除键值对元素并把值返回

3、判断功能
boolean containsKey(Object key);//判断集合中是否包含指定的键
boolean containValue(Object value);//判断集合是否包含指定的值
boolean isEmpty();//判断集合是否为空

4、长度功能
int size();//返回集合中键值对的对数

5、获取功能
v get(Object key);//根据键来获取值
Set<K> keySet();//获取集合中所有键的集合
Collection<V> values();//获取集合中所有值的集合
Set<Map.Entry<K,V>> entrySet();//返回的是键值对对象的集合

24
24

HashMap

允许使用null键和null值,与HashSet一样,不保证映射的顺序。

所有的key构成的集合是Set:无序的、不可重复的。所以,key所在的类要重写:equals()和hashCode(),所有的value构成的集合是Collection:无序的、可以重复的。所以,value所在的类要重写:equals(),一个key-value构成一个entry,所有的entry构成的集合是Set:无序的、不可重复的,HashMap 判断两个 key 相等的标准是:两个 key 通过 equals() 方法返回 true,hashCode 值也相等。HashMap 判断两个 value 相等的标准是:两个 value 通过 equals() 方法返回 true。

25
25
26
26
HashMap 源码中的重要常量

DEFAULT_INITIAL_CAPACITY : HashMap的默认容量,16 MAXIMUM_CAPACITY : : HashMap的最大支持容量,2^30 DEFAULT_LOAD_FACTOR :HashMap的默认加载因子 TREEIFY_THRESHOLD :Bucket中链表长度大于该默认值,转化为红黑树 UNTREEIFY_THRESHOLD :Bucket中红黑树存储的Node小于该默认值,转化为链表 MIN_TREEIFY_CAPACITY :桶中的Node被树化时最小的hash表容量。(当桶中Node的 数量大到需要变红黑树时,若hash表容量小于MIN_TREEIFY_CAPACITY时,此时应执行 resize扩容操作这个MIN_TREEIFY_CAPACITY的值至少是TREEIFY_THRESHOLD的4 倍。) table :存储元素的数组,总是2的n次幂 entrySet: :存储具体元素的集 size :HashMap中存储的键值对的数量 modCount :HashMap扩容和结构改变的次数。 threshold :扩容的临界值,=容量*填充因子 loadFactor: :填充因子

HashMap的内部存储结构

是 数组+ 链表+ 树 的结合。当实例化一个HashMap时,会初始化initialCapacity和loadFactor,在put第一对映射关系时,系统会创建一个长度为initialCapacity的Node数组,这个长度在哈希表中被称为容量(Capacity),在这个数组中可以存放元素的位置我们称之为“桶”(bucket),每个bucket都有自己的索引,系统可以根据索引快速的查找bucket中的元素。每个bucket中存储一个元素,即一个Node对象,但每一个Node对象可以带一个引用变量next,用于指向下一个元素,因此,在一个桶中,就有可能生成一个Node链。也可能是一个一个TreeNode对象,每一个TreeNode对象可以有两个叶子结点left和right,因此,在一个桶中,就有可能生成一个TreeNode树。而新添加的元素作为链表的last,或树的叶子结点。当HashMap中的其中一个链的对象个数如果达到了8个,此时如果capacity没有达到64,那么HashMap会先扩容解决,如果已经达到了64,那么这个链会变成树,结点类型由Node变成TreeNode类型。当然,如果当映射关系被移除后,下次resize方法时判断树的结点个数低于6个,也会把树再转为链表。

LinkedHashMap

LinkedHashMap 是 HashMap 的子类在HashMap存储结构的基础上,使用了一对双向链表来记录添加元素的顺序,与LinkedHashSet类似,LinkedHashMap 可以维护 Map 的迭代顺序:迭代顺序与 Key-Value 对的插入顺序一致

HashMap中的内部类:Node

static class Node<K,V> implements Map.Entry<K,V> {
final int hash;
final K key;
V value;
Node<K,V> next;
}

LinkedHashMap中的内部类:Entry

static class Entry<K,V> extends HashMap.Node<K,V> {
Entry<K,V> before, after;
Entry(int hash, K key, V value, Node<K,V> next) {
super(hash, key, value, next);
}
}

TreeMap

TreeMap存储 Key-Value 对时,需要根据 key-value 对进行排序。TreeMap 可以保证所有的 Key-Value 对处于 有序状态。TreeSet底层使用 红黑树结构存储数据 TreeMap 的 Key 的排序: 自然排序:TreeMap 的所有的 Key 必须实现 Comparable 接口,而且所有的 Key 应该是同一个类的对象,否则将会抛出 ClasssCastException 定制排序:创建 TreeMap 时,传入一个 Comparator 对象,该对象负责对TreeMap 中的所有 key 进行排序。此时不需要 Map 的 Key 实现 Comparable 接口 TreeMap判断 两个key 相等的标准:两个key通过compareTo()方法或 者compare()方法返回0。

Hashtable

Hashtable是个古老的 Map 实现类,JDK1.0就提供了。不同于HashMap,Hashtable是线程安全的。Hashtable实现原理和HashMap相同,功能相同。底层都使用哈希表结构,查询速度快,很多情况下可以互用。 与HashMap不同,Hashtable 不允许使用 null 作为 key 和 value 与HashMap一样,Hashtable 也不能保证其中 Key-Value 对的顺序 Hashtable判断两个key相等、两个value相等的标准,与HashMap一致。

Properties

Properties 类是 Hashtable 的子类,该对象用于处理属性文件由于属性文件里的 key、value 都是字符串类型,所以 Properties 里的 key和 value 都是字符串类型,存取数据时,建议使用setProperty(String key,String value)方法和getProperty(String key)方法

Properties pros = new Properties();
pros.load(new FileInputStream("jdbc.properties"));
String user = pros.getProperty("user");
System.out.println(user);

Collections工具类

查找、替换
Object max(Collection):根据元素的自然顺序,返回给定集合中的最大元素
Object max(CollectionComparator):根据 Comparator 指定的顺序,返回给定集合中的最大元素
Object min(Collection)
Object min(CollectionComparator)
int frequency(CollectionObject):返回指定集合中指定元素的出现次数
void copy(List dest,List src):将src中的内容复制到dest中
boolean replaceAll(List list,Object oldVal,Object newVal):使用新值替换List 对象的所有旧值

多线程

程序(program)是为完成特定任务、用某种语言编写的一组指令的集合。即指一段静态的代码,静态对象。 进程(process)是程序的一次执行过程,或是正在运行的一个程序。是一个动态的过程:有它自身的产生、存在和消亡的过程。——生命周期,如:运行中的QQ,运行中的MP3播放器,程序是静态的,进程是动态的,进程作为资源分配的单位,系统在运行时会为每个进程分配不同的内存区域。线程(thread),进程可进一步细化为线程,是一个程序内部的一条执行路径。若一个进程同一时间 并行执行多个线程,就是支持多线程的,线程作为调度和执行的单位,每个线程拥有独立的运行栈和程序计数器(pc),线程切换的开销小,一个进程中的多个线程共享相同的内存单元/内存地址空间它们从同一堆中分配对象,可以访问相同的变量和对象。这就使得线程间通信更简便、高。但多个线程操作共享的系统资源可能就会带来安全的隐患。

一个Java应用程序java.exe,其实至少有三个线程:main()主线程,gc()垃圾回收线程,异常处理线程。当然如果发生异常,会影响主线程。

并行与并发:

并行:多个CPU同时执行多个任务。比如:多个人同时做不同的事。

并发:一个CPU(采用时间片)同时执行多个任务。比如:秒杀、多个人做同一件事。

Thread类
Thread() :创建新的Thread对象
Thread(String threadname): :创建线程并指定线程实例名
Thread(Runnable target) :指定创建线程的目标对象,它实现了Runnable接中的run方法
Thread(Runnable target, String name) :创建新的Thread对象

方式一: 继承Thread类

1 定义子类继承Thread类。 2子类中重写Thread类中的run方法。 3 创建Thread子类对象,即创建了线程对象。 4)调用线程对象start方法:启动线程,调用run方法。

27
27
Thread 类的有关法 方法
void start(): 启动线程,并执行对象的run()方法
run(): 线程在被调度时执行的操作
String getName(): 返回线程的名称
void setName(String name):设置该线程名称
static Thread currentThread(): 返回当前线程。在Thread子类中就是this,通常用于主线程和Runnable实现类
static void yield(): : 线程让步
暂停当前正在执行的线程,把执行机会让给优先级相同或更高的线程,若队列中没有同优先级的线程,忽略此方法

join() : : 当某个程序执行流中调用其他线程的 join() 方法时,调用线程将被阻塞,直到 join() 方法加入的 join 线程执行完为止,低优先级的线程也可以获得执行

static void sleep(long millis)(指定时间:毫秒)
令当前活动线程在指定时间段内放弃对CPU控制,使其他线程有机会被执行,时间到后重排队。
抛出InterruptedException异常
stop(): 强制线程生命期结束,不推荐使用
boolean isAlive(): : 返回boolean,判断线程是否还活着

方式二:实现Runnable

1 定义子类,实现Runnable接口。 2)子类中重写Runnable接口中的run方法。 3通过Thread类含参构造器创建线程对象。 4 将Runnable接口的子类对象作为实际参数传递给Thread类的构造器中。 5调用Thread类的start方法:开启线程,调用Runnable子类接口的run方法。

继承方式和实现方式的联系与 区别

public class Thread extends Object implements Runnable
package Demo02;

import javax.security.auth.callback.TextInputCallback;

public class Race implements Runnable{
   private String winner;

   @Override
   public void run() {
       int i = 0;
       while(i <= 100){
           boolean flag = gameOver(i);//判断游戏是否继续
           if(flag)
               break;
           System.out.println(Thread.currentThread().getName() + "跑了" + i + "米");
           if (Thread.currentThread().getName().equals("兔子")) {
               try {
                   Thread.sleep(1);//休眠时间,模仿兔子休息
                   i += 5; //设定兔子的速度
               } catch (InterruptedException e) {
                   e.printStackTrace();
               }
           }
           else
               i += 1;
       }
   }

   private boolean gameOver(int step){ //判断游戏是否结束
       if(winner != null) //还未出现胜利者
           return true;
       else if(step == 100){
           winner = Thread.currentThread().getName();//出现胜利者,为winner赋值
           System.out.println(winner + "跑了100米");
           System.out.println("龟兔赛跑比赛的胜利者是" + winner );
           return true;
       }
       else
           return false;
   }

   public static void main(String[] args) {
       Race race = new Race();
       new Thread(race,"兔子").start();
       new Thread(race,"乌龟").start();
   }
}

区别

继承Thread:线程代码存放Thread子类run方法中。 实现Runnable:线程代码存在接口的子类的run方法。

实现方式的好处

避免了单继承的局限性,多个线程可以共享同一个接口实现类的对象,非常适合多个相同线程来处理同一份资源。

j

java 的调度方法

同优先级线程组成先进先出队列(先到先服务),使用时间片策略,对高优先级,使用优先调度的抢占式策略

线程 的 优先级等级
MAX_PRIORITY10
MIN _PRIORITY :1
NORM_PRIORITY5
涉及的方法
getPriority() : :返回线程优先值
setPriority(int newPriority) : :改变线程的优先级

说明 线程创建时继承父线程的优先级 低优先级只是获得调度的概率低,并非一定是在高优先级线程之后才被调用

Java中的线程分为两类:一种是 守护线程,一种是 用户线程。

它们在几乎每个方面都是相同的,唯一的区别是判断JVM何时离开。 守护线程是用来服务用户线程的,通过在start()方法前调用 thread.setDaemon(true)可以把一个用户线程变成一个守护线程。 Java垃圾回收就是一个典型的守护线程。 若JVM中都是守护线程,当前JVM将退出。

JDK 中用Thread.State 类定义了 线程的 五种状态

新建: 当一个Thread类或其子类的对象被声明并创建时,新生的线程对象处于新建状态

就绪:处于新建状态的线程被start()后,将进入线程队列等待CPU时间片,此时它已具备了运行的条件,只是没分配到CPU资源

运行:当就绪的线程被调度并获得CPU资源时,便进入运行状态, run()方法定义了线程的操作和功能

阻塞:在某种特殊情况下,被人为挂起或执行输入输出操作时,让出 CPU 并临时中止自己的执行,进入阻塞状态

死亡:线程完成了它的全部工作或线程被提前强制性地中止或出现异常导致结束

28
28
29
29
线程的同步
synchronized
1. 同步代码 块 :
synchronized ( 对象){
// 需要被同步的代码;
}
2. synchronized 还可以放在方法声明中,表示 整个 方法为 同步方法 。
例如:
public synchronized void show (String name){.
}

单例设计式 模式之懒汉式( 线程安全)

lass Singleton {
private static Singleton instance = null;
private Singleton(){}
public static Singleton getInstance(){
if(instance==null){
synchronized(Singleton.class){
if(instance == null){
instance=new Singleton();
} } }
return instance;
} }
public class SingletonTest{
public static void main(String[] args){
Singleton s1=Singleton.getInstance();
Singleton s2=Singleton.getInstance();
System.out.println(s1==s2);
} }
Lock( 锁)
class A{
private final ReentrantLock lock = new ReenTrantLock();
public void m(){
lock.lock();
try{
// 保证线程安全的代码;
}
finally{
lock.unlock();
}
}
}
注意:如果同步代码有异常,要将unlock()写入finally语句块

synchronized 与 Lock 的对比

  1. Lock是显式锁(手动开启和关闭锁,别忘记关闭锁),synchronized是隐式锁,出了作用域自动释放

  2. Lock只有代码块锁,synchronized有代码块锁和方法锁

  3. 使用Lock锁,JVM将花费较少的时间来调度线程,性能更好。并且具有更好的扩展性(提供更多的子类) 优先使用顺序: Lock 同步代码块(已经进入了方法体,分配了相应资源) >同步方法(在方法体之外)

线程 的 通信
lass Communication implements Runnable {
int i = 1;
public void run() {
while (true) {
synchronized (this) {
notify();
if (i <= 100) {
System.out.println(Thread.currentThread().getName() +
":" + i++);
} else
break;
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}}}}}

wait() 与 与 notify() 和 和 notifyAll() wait():令当前线程挂起并放弃CPU、同步资源并等待,使别的线程可访问并修改共享资源,而当 前线程排队等候其他线程调用notify()或notifyAll()方法唤醒,唤醒后等待重新获得对监视器的所有 权后才能继续执行。 notify():唤醒正在排队等待同步资源的线程中优先级最高者结束等待 notifyAll ():唤醒正在排队等待资源的所有线程结束等待. 这三个方法只有在synchronized方法或synchronized代码块中才能使用,否则会报java.lang.IllegalMonitorStateException异常。 因为这三个方法必须有锁对象调用,而任意对象都可以作为synchronized的同步锁,因此这三个方法只能在Object类中声明。

notify()/notifyAll(),当前线程中调用方法: 对象名.notify(),功能:唤醒等待该对象监控权的一个/所有线程。

调用方法的必要条件:当前线程必须具有对该对象的监控权(加锁)

方式三实现Callable

与使用Runnable相比, Callable功能更强大,相比run()方法,可以有返回值,方法可以抛出异常,支持泛型的返回值,需要借助FutureTask类,比如获取返回结果

Future接口

可以对具体Runnable、Callable任务的执行结果进行取消、查询是否完成、获取结果等。 FutrueTask是Futrue接口的唯一的实现类 FutureTask 同时实现了Runnable, Future接口。它既可以作为Runnable被线程执行,又可以作为Future得到Callable的返回值

流程:

  • 实现Callable接口,需要返回值类型
  • 重写call方法,需要抛出异常
  • 创建目标对象
  • 创建执行服务:ExecutorService ser = Executors.newFixedThreadPool(1)
  • 提交执行Future result1 = ser.submit(t1);
  • 获取结果:boolean r1 = resut1.get()
  • 关闭服务:ser.shutdownNow()
//创建线程法3-实现Callable接口
//callable 优点:可以定义返回值,可以抛出异常
//缺点是实现代码较为繁琐复杂,效率不如前两者高
public class TheradCallable implements Callable<Boolean> {

    private String url;
    private String name;

    public TheradCallable(String url,String name){
        this.url=url;
        this.name=name;
    }
    @Override
    public Boolean call() throws Exception {
        WebDownloader02 webDownloader02=new WebDownloader02();
        webDownloader02.downloader(url,name);
        System.out.println("下载了文件名为:"+name);
        return true;
    }
    public static void main(String[] args) throws ExecutionException, InterruptedException {
        TheradCallable testThread201=new TheradCallable("http://");
        TheradCallable testThread202=new TheradCallable("http://");
        TheradCallable testThread203=new TheradCallable("http://");
        //创建执行服务
        ExecutorService ser = Executors.newFixedThreadPool(3);
        //提交执行
        Future<Boolean> result1 = ser.submit(testThread201);
        Future<Boolean> result2 = ser.submit(testThread202);
        Future<Boolean> result3 = ser.submit(testThread203);
        //获取结果
        boolean r1 = result1.get();
        boolean r2 = result2.get();
        boolean r3 = result3.get();
        //关闭服务
        ser.shutdownNow();
    }
}

class WebDownloader02{
    public void downloader(String url,String name){
        try {
            FileUtils.copyURLToFile(new URL(url),new File(name));
        } catch (IOException e) {
            e.printStackTrace();
            System.out.println("IO异常,downder出现问题");
        }
    }
}

结果展示#
下载了文件名为:portfolio-2.jpg
下载了文件名为:portfolio-3.jpg
下载了文件名为:portfolio-1.jpg

方式四使用线程池

提前 创建好多个线程,放入线程池中,使用时直接获取,使用完放回池中。可以避免频繁创建销毁、实现重复利用。

提高响应速度(减少了创建新线程的时间) 降低资源消耗(重复利用线程池中线程,不需要每次都创建)便于线程管理 corePoolSize:核心池的大小 maximumPoolSize:最大线程数 keepAliveTime:线程没有任务时最多保持多长时间后会终止

ExecutorService:真正的线程池接口。常见子类ThreadPoolExecutor
void execute(Runnable command) :执行任务/命令,没有返回值,一般用来执行Runnable
<T> Future<T> submit(Callable<T> task):执行任务,有返回值,一般又来执行Callable

void shutdown() :关闭连接池
Executors:工具类、线程池的工厂类,用于创建并返回不同类型的线程池
Executors.newCachedThreadPool():创建一个可根据需要创建新线程的线程池
Executors.newFixedThreadPool(n); 创建一个可重用固定线程数的线程池Executors.newSingleThreadExecutor() :创建一个只有一个线程的线程池 Executors.newScheduledThreadPool(n):创建一个线程池,它可安排在给定延迟后运
行命令或者定期地执行。

网络通信

IP和端口号

网络通信协议

36
36
37
37

IP 地址:InetAddress 唯一的标识 Internet 上的计算机(通信实体) 本地回环地址(hostAddress):127.0.0.1 主机名(hostName):localhost IP地址分类方式1:IPV4 和 IPV6 IPV4:4个字节组成,4个0-255。大概42亿,30亿都在北美,亚洲4亿。2011年初已经用尽。以点分十进制表示,如192.168.0.1 IPV6:128位(16个字节),写成8个无符号整数,每个整数用四个十六进制位表示,数之间用冒号(:)分开,如:3ffe:3201:1401:1280:c8ff:fe4d:db39:1984 IP地址分类方式2: 公网地址( 万维网使用)和 私有地址( 局域网使用)。192.168.开头的就是私有址址,范围即为192.168.0.0--192.168.255.255,专门为组织机构内部使用

端口 号标识正在计算机上运行的进程(程序) 不同的进程有不同的端口号 被规定为一个 16 位的整数 0~65535。 端口分类: 公认端口:0~1023。被预先定义的服务通信占用(如:HTTP占用端口80,FTP占用端口21,Telnet占用端口23) 注册端口:1024~49151。分配给用户进程或应用程序。(如:Tomcat占用端口8080,MySQL占用端口3306,Oracle占用端口1521等)。 动态/ 私有端口:49152~65535。 端口与 号与IP 地址的组合得出一个网络套: 接字:Socket。

InetAddress类

Internet上的主机有两种方式表示地址:域名(hostName):www.atguigu.comopen in new window,IP 地址(hostAddress):202.108.35.210 InetAddress类主要表示IP地址,两个子类:Inet4Address、Inet6Address。 InetAddress 类对象含有一个 Internet 主机地址的域名和IP地址:www.atguigu.comopen in new window 和 202.108.35.210。 域名容易记忆,当在连接网络时输入一个主机的域名后,域名服务器(DNS),负责将域名转化成IP地址,这样才能和主机建立连接

netAddress 类没有提供公共的构造器,而是提供 了 如下几个 静态方法来获取
InetAddress 实例
public static InetAddress getLocalHost()
public static InetAddress getByName(String host)
InetAddress 提供了如下几个常用 的 方法
public String getHostAddress() :返回 IP 地址字符串(以文本表现形式)。
public String getHostName() :获取此 IP 地址的主机名
public boolean isReachable(int timeout): :测试是否可以达到该地址

38
38

TCP/IP 协议簇

传输层协议中有两个非常重要的协议: 传输控制协议TCP(Transmission Control Protocol) 用户数据报协议UDP(User Datagram Protocol)。 TCP/IP 以其两个主要协议:传输控制协议(TCP) 和网络互联协议(IP)而得 名,实际上是一组协议,包括多个具有不同功能且互为关联的协议。 IP(Internet Protocol)协议是网络层的主要协议,支持网间互连的数据通信。 TCP/IP协议模型从更实用的角度出发,形成了高效的四层体系结构,即、 物理链路层、IP 层、传输层和应用层

TCP 和 和 UDP

TCP 协议:

使用TCP协议前,须先建立TCP连接,形成传输数据通道 传输前,采用“ 三次握手”方式,点对点通信,是可靠的 TCP协议进行通信的两个应用进程:客户端、服务端。 在连接中可进行大数据量的传输 传输完毕,需释放已建立的连接,效率低 UDP 协议: 将数据、源、目的封装成数据包,不需要建立连接 每个数据报的大小限制在64K内 发送不管对方是否准备好,接收方收到也不确认,故是不可靠的 可以广播发送 发送数据结束时无需释放资源,开销小,速度快

38
38
39
39

Socket

用套接字(Socket)开发网络应用程序早已被广泛的采用,以至于成为事实上的标准。 网络上具有唯一标识的IP地址和端口号组合在一起才能构成唯一能识别的标识符套接字。 通信的两端都要有Socket,是两台机器间通信的端点。 网络通信其实就是Socket间的通信。 Socket允许程序把网络连接当成一个流,数据在两个Socket间通过IO传输。 一般主动发起通信的应用程序属客户端,等待通信请求的为服务端。 Socket分类: 流套接字(stream socket):使用TCP提供可依赖的字节流服务 数据报套接字(datagram socket):使用UDP提供“尽力而为”的数据报服务

Socket 类的常用 构造 器 :
public Socket(InetAddress address,int port)创建一个流套接字并将其连接到指定IP 地址的指定端口。
public Socket(String host,int port)创建一个流套接字并将其连接到指定主机上的指定端口号。
Socket 类的常用方法:
public InputStream getInputStream()返回此套接字的输入流。可以用于接收网络消息
public OutputStream getOutputStream()返回此套接字的输出流。可以用于发送网络消息
public InetAddress getInetAddress()此套接字连接到的远程 IP 地址;如果套接字是未连接的,则返回nullpublic InetAddress getLocalAddress()获取套接字绑定的本地地址。 即本端的IP地址
public int getPort()此套接字连接到的远程端口号;如果尚未连接套接字,则返回0public int getLocalPort()返回此套接字绑定到的本地端口。 如果尚未绑定套接字,则返回 -1。即本端的
端口号。
public void close()关闭此套接字。套接字被关闭后,便不可在以后的网络连接中使用(即无法重新连接
或重新绑定)。需要创建新的套接字对象。 关闭此套接字也将会关闭该套接字的 InputStreamOutputStreampublic void shutdownInput()如果在套接字上调用 shutdownInput() 后从套接字输入流读取内容,则流将
返回 EOF(文件结束符)。即不能在从此套接字的输入流中接收任何数据。
public void shutdownOutput()禁用此套接字的输出流。对于 TCP 套接字,任何以前写入的数据都将被发
送,并且后跟 TCP 的正常连接终止序列。 如果在套接字上调用 shutdownOutput() 后写入套接字输出流,
则该流将抛出 IOException。 即不能通过此套接字的输出流发送任何数据。
基于Socket 的TCP 编程

客户端Socket 的工作过程包含以下四个基本的步骤 : 创建 Socket :根据指定服务端的 IP 地址或端口号构造 Socket 类对象。若服务器端响应,则建立客户端到服务器的通信线路。若连接失败,会出现异常。 打开连接到 Socket 的输入/ 出流: 使用 getInputStream()方法获得输入流,使用getOutputStream()方法获得输出流,进行数据传输 按照一定的协议对 Socket 进行读/ 写操作:通过输入流读取服务器放入线路的信息(但不能读取自己放入线路的信息),通过输出流将信息写入线程。 关闭 Socket: :断开客户端到服务器的连接,释放线路

客户端创建Socket 对象

客户端程序可以使用Socket类创建对象, 创建的同时会自动向服务器方发起连 接 接。Socket的构造器是: Socket(String host,int port)throws UnknownHostException,IOException:向服务器(域名是 host。端口号为port)发起TCP连接,若成功,则创建Socket对象,否则抛出异常。 Socket(InetAddress address,int port)throws IOException:根据InetAddress对象所表示的 IP地址以及端口号port发起连接。 客户端建立socketAtClient对象的过程就是向服务器发出套接字连接请求

Socket s = new
Socket(192.168.40.165,9999);
OutputStream out = s.getOutputStream();
out.write(" hello".getBytes());
s.close();
服务器 程序的工作过程包含以下四个基本的 步骤

用调用 ServerSocket(int port) : :创建一个服务器端套接字,并绑定到指定端口上。用于监听客户端的请求。

调用 accept(): :监听连接请求,如果客户端请求连接,则接受连接,返回通信套接字对象。

调用 该 该Socket 类对象的 getOutputStream() 和 和 getInputStream (): :获取输出 流和输入流,开始网络数据的发送和接收。

关闭ServerSocket 和Socket 对象:客户端访问结束,关闭通信套接字。

服务器建立 ServerSocket 对象

ServerSocket 对象负责等待客户端请求建立套接字连接,类似邮局某个窗口 中的业务员。也就是说, 服务器必须事先建立一个等待客户请求建立套接字 的 连接的ServerSocket 对象。 所谓“接收”客户的套接字请求,就是accept()方法会返回一个 Socket 对象

ServerSocket ss = new ServerSocket(9999);
Socket s = ss.accept ();
InputStream in = s.getInputStream();
byte[] buf = new byte[1024];
int num = in.read(buf);
String str = new String(buf,0,num);
System.out.println(s.getInetAddress().toString()+:+str);
s.close();
ss.close();
40
40

客户端(自定义,浏览器)— 服务端(自定义,Tomcat服务器)

UDP网络编程

UDP网络通信

类 DatagramSocket 和 DatagramPacket 实现了基于 UDP 协议网络程序。 UDP数据报通过数据报套接字 DatagramSocket 发送和接收,系统不保证UDP数据报一定能够安全送到目的地,也不能确定什么时候可以抵达。 DatagramPacket 对象封装了UDP数据报,在数据报中包含了发送端的IP地址和端口号以及接收端的IP地址和端口号。 UDP协议中每个数据报都给出了完整的地址信息,因此无须建立发送方和接收方的连接。如同发快递包裹一样。

DatagramSocket 类的常用方法

public DatagramSocket(int port)创建数据报套接字并将其绑定到本地主机上的指定端口。套接字将被
绑定到通配符地址,IP 地址由内核来选择。
public DatagramSocket(int port,InetAddress laddr)创建数据报套接字,将其绑定到指定的本地地址。
本地端口必须在 065535 之间(包括两者)。如果 IP 地址为 0.0.0.0,套接字将被绑定到通配符地
址,IP 地址由内核选择。
public void close()关闭此数据报套接字。
public void send(DatagramPacket p)从此套接字发送数据报包。DatagramPacket 包含的信息指示:将
要发送的数据、其长度、远程主机的IP 地址和远程主机的端口号。
public void receive(DatagramPacket p)从此套接字接收数据报包。当此方法返回时,DatagramPacket
的缓冲区填充了接收的数据。数据报包也包含发送方的 IP 地址和发送方机器上的端口号。 此方法
在接收到数据报前一直阻塞。数据报包对象的 length 字段包含所接收信息的长度。如果信息比包的
长度长,该信息将被截短。
public InetAddress getLocalAddress()获取套接字绑定的本地地址。
public int getLocalPort()返回此套接字绑定的本地主机上的端口号。
public int getPort()返回此套接字的端口。如果套接字未连接,则返回-1public DatagramPacket(byte[] buf,int length)构造 DatagramPacket,用来接收长度为 length 的数据包。 length 参数必须小于等于 buf.length。
public DatagramPacket(byte[] buf,int length,InetAddress address,int port)构造数据报包,用来将长度为 length 的包发送到指定主机上的指定端口号。length
参数必须小于等于 buf.length。
public InetAddress getAddress()返回某台机器的 IP 地址,此数据报将要发往该机器或者是从该机器接收到的。
public int getPort()返回某台远程主机的端口号,此数据报将要发往该主机或者是从该主机接收到的。
public byte[] getData()返回数据缓冲区。接收到的或将要发送的数据从缓冲区中的偏移量 offset 处开始,持续 length 长度。
public int getLength()返回将要发送或接收到的数据的长度。

流 程:

  1. DatagramSocket与DatagramPacket

  2. 建立发送端,接收端

  3. 建立数据包

  4. 调用Socket的发送、接收方法

  5. 关闭Socket

发送端与接收端是两个独立的运行程序

发送端

DatagramSocket ds = null;
try {
ds = new DatagramSocket();
byte[] by = "hello,atguigu.com".getBytes();
DatagramPacket dp = new DatagramPacket(by, 0, by.length,
InetAddress.getByName("127.0.0.1"), 10000);
ds.send(dp);
} catch (Exception e) {
e.printStackTrace();
} finally {
if (ds != null)
ds.close();
}

接收端

要指定监听的 端口 。

DatagramSocket ds = null;
try {
ds = new DatagramSocket(10000);
byte[] by = new byte[1024];
DatagramPacket dp = new DatagramPacket(by, by.length);
ds.receive(dp);
String str = new String(dp.getData(), 0, dp.getLength());
System.out.println(str + "--" + dp.getAddress());
} catch (Exception e) {
e.printStackTrace();
} finally {
if (ds != null)
ds.close();
}

URL类

URL(Uniform Resource Locator):统一资源定位符,它表示 Internet 上 某一 资源的地址。 它是一种具体的URI,即URL可以用来标识一个资源,而且还指明了如何locate 这个资源。 通过 URL 我们可以访问 Internet 上的各种网络资源,比如最常见的 www,ftp 站点。浏览器通过解析给定的 URL 可以在网络上查找相应的文件或其他资源。 URL的基本结构由5部分组成: < 传输协议>://< 主机名>:< 端口号>/< 文件名># 片段名? 参数列表 例如:http://192.168.1.100:8080/helloworld/index.jsp#a?username=shkstart&password=123open in new window #片段名:即锚点,例如看小说,直接定位到章节 参数列表格式:参数名=参数值&参数名=参数值....

为了表示URL,java.netopen in new window 中实现了类 URL。我们可以通过下面的构造器来初 始化一个 URL 对象:

public URL (String spec):通过一个表示URL地址的字符串可以构造一个URL对象。例
如:URL url = new URL ("http://www. atguigu.com/");
public URL(URL context, String spec):通过基 URL 和相对 URL 构造一个 URL 对象。
例如:URL downloadUrl = new URL(url, “download.html")
public URL(String protocol, String host, String file); 例如:new URL("http",
"www.atguigu.com", “download. html");
public URL(String protocol, String host, int port, String file); 例如: URL gamelan = new
URL("http", "www.atguigu.com", 80, “download.html");

URL类的构造器都声明抛出非运行时异常,必须要对这一异常进行处理,通常是用 try-catch 语句进行捕获。

一个URL对象生成后,其属性是不能被改变的,但可以通过它给定的方法来获取这些属性:

public String getProtocol( ) 获取该URL的协议名
public String getHost( ) 获取该URL的主机名
public String getPort( ) 获取该URL的端口号
public String getPath( ) 获取该URL的文件路径
public String getFile( ) 获取该URL的文件名
public String getQuery( ) 获取该URL的查询名
URL url = new URL("http://localhost:8080/examples/myTest.txt");
System.out.println("getProtocol() :"+url.getProtocol());
System.out.println("getHost() :"+url.getHost());
System.out.println("getPort() :"+url.getPort());
System.out.println("getPath() :"+url.getPath());
System.out.println("getFile() :"+url.getFile());
System.out.println("getQuery() :"+url.getQuery());

针对HTTP 协议的URLConnection类

URL的方法 openStream():能从网络上读取数据
若希望输出数据,例如向服务器端的 CGI (公共网关接口-Common Gateway
Interface-的简称,是用户浏览器和服务器端的应用程序进行连接的接口)程序发送一
些数据,则必须先与URL建立连接,然后才能对其进行读写,此时需要使用
URLConnectionURLConnection:表示到URL所引用的远程对象的连接。当与一个URL建立连接时,
首先要在一个 URL 对象上通过方法 openConnection() 生成对应的 URLConnection
对象。如果连接过程失败,将产生IOException.
URL netchinaren = new URL ("http://www.atguigu.com/index.shtml");
URLConnectonn u = netchinaren.openConnection( );
通过URLConnection对象获取的输入流和输出流,即可以与现有的CGI
程序进行交互。
public Object getContent( ) throws IOException
public int getContentLength( )
public String getContentType( )
public long getDate( )
public long getLastModified( )
public InputStream getInputStream( )throws IOException
public OutputSteram getOutputStream( )throws IOException

URI 、URL 和URN 的区别

URI,是uniform resource identifier,统一资源标识符,用来唯一的标识一个资源。而URL是uniform resource locator,统一资源定位符,它是一种具体的URI,即URL可以用来标识一个资源,而且还指明了如何locate这个资源。而URN,uniform resource name,统一资源命名,是通过名字来标识资源,比如mailto:java-net@java.sun.com。也就是说,URI是以一种抽象的,高层次概念定义统一资源标识,而URL和URN则是具体的资源标识的方式。URL和URN都是一种URI。在Java的URI中,一个URI实例可以代表绝对的,也可以是相对的,只要它符合URI的语法规则。而URL类则不仅符合语义,还包含了定位该资源的信息,因此它不能是相对的。

常用类

String

**String类 **: 代表 字符串。Java 程序中的所有字符串字面值(如 "abc" )都作 为此类的实例实现。 String是一个final类,代表不可变的字符序列。 字符串是常量,用双引号引起来表示。它们的值在创建之后不能更改。 String对象的字符内容是存储在一个字符数组value[]中的。

public final class String
implements java.io.Serializable, Comparable<String>, CharSequence {
/** The value is used for character storage. */
private final char value[];
/** Cache the hash code for the string */
private int hash; // Default to 0
30
30
31
31
.创建一个字符串

    String str="123456abc";

    String str1=new String("123456abc");

1.equals(String str)方法,比较两个字符串的值是否一致,返回值为boolean类型

    System.out.println(str.equals("123abcd"));//打印为false

    System.out.println(str.equals("123456abc"));//打印为true

2.length()方法,返回字符串的长度

   System.out.println(str.length());//打印为9

3.charAt(int index)从字符中取出指定索引的值

   System.out.println(str.charAt(1));//打印为2

4.indexOf(String str)方法,查找对应字符在字符串中的索引位置,如果没有则返回-1,常与3配合使用,

   System.out.println(str.indexOf("3"));//打印为2

5.lastIndexOf(String str)方法,查找对应字符最后在字符串中出现的索引位置,如果没有则返回-1

   System.out.println(str.indexOf("3"));//打印为2

6.toCharArray()方法,将字符串变成一个数组

  char[] chars = str.toCharArray();
  for (int i=0;i<chars.length;i++){
      System.out.println(chars[i]);
  }//输出结果为一个字符一行的打印

7.toUpperCase()将字符串全部转换为大写

  String sr="Signal-strength";
  String s = sr.toUpperCase();
  System.out.println(s);//打印为SIGNAL-STRENGTH

8.toLowerCase()将字符串全部转换为小写

  String sr="Signal-strength";
  String s = sr.toLowerCase();
  System.out.println(s);//打印为signal-strength

9.split("字符"),根据给定的正则表达式来拆分字符串,形成一个String数组

  String str="123_456";
  String[] s = str.split("_");
  for (String s1:s){
      System.out.println(s1);
  }

打印为

  123
  456

10.trim()方法,去除字符串左右两端的空白,该方法只能去除左右,中间的没办法

  String str=" 1 23_456 ";
  System.out.println(str);
  System.out.println(str.trim());

打印为

 1 23_456 
1 23_456

11.substring(int beginIndex,int endIndex)截取字符串

String str="123_456";
System.out.println(str.substring(0,3));//不包含3
System.out.println(str.substring(2));//从第3个位置截取

打印为

123
3_456

12.equalsIgnoreCase(String str),忽略字符串大小比较字符串的值,

  String str="absC";
  String str1="absc";
  System.out.println(str.equalsIgnoreCase(str1));//打印为true

13.concat(String str),将str的字符串的内容添加到字符串的后面,效果等同于+

 String str="absC";
 String str1="absc";
 System.out.println(str.concat(str1));//打印为absCabsc

    等同于

 String str="absC"+"absc";

14.replace(char oldChar,char newChar),该方法用字符newChar替换掉当前字符串中所有的oldChar。

String str="absCac";
System.out.println(str.replace("a","c"));//打印为cbsCcc

15.replaceFirst(String regex,String replacement),该方法用字符replacement替换掉当前字符串中第一个匹配regex。

String str="absCac";
System.out.println(str.replaceFirst("a","c"));//打印为cbsCac

16.replaceAll(String regex,String replacement),该方法用replacement替换掉当前字符串中第一个匹配regex。

String str="absCac";
System.out.println(str.replace("a","c"));//打印为cbsCcc

17.startsWith(String prefix),比较该字符串是否以prefix子字符串开始的

String str="absCac";
System.out.println(str.startsWith("ab"));//打印为true

18.endsWith(String prefix),比较该字符串是否以prefix结尾的

String str="absCac";
System.out.println(str.endsWith("ab"));//打印为false

19.valueOf(Type type)用于将基本数据类型转换为String类型,补充一点,type不能为null,不然会报空指针异常

String s = String.valueOf(21);
System.out.println(s);//打印为21

20.getBytes(),将该字符串转换为字节数组

String str="123421421";
byte[] bytes = str.getBytes();

21.String.format()方法,字符串类型格式话

     format(String format,Object obj),新字符串使用本地语言环境,制定字符串格式和参数生成格式的新字符串

     format(Locale locale,String format,Object obj),使用指定语言环境,制定字符串格式和参数生成格式的新字符串

    显示不同转换符实现不同数据类型转换

字符串 > 基本数据类型、包装类

nteger包装类的public static int parseInt(String s):可以将由“数字”字符组成的字符串转换为整型

使用java.lang包中的Byte、Short、Long、Float、Double类调相应的类方法可以将由“数字”字符组成的字符串,转化为相应的基本数据类型

基本 数据类型、包装类 >字符串

调用String类的public String valueOf(int n)可将int型转换为字符串

相应的valueOf(byte b)、valueOf(long l)、valueOf(float f)、valueOf(doubled)、valueOf(boolean b)可由参数的相应类型到字符串的转换

字符串转字符数组

String myString = "hello123"; //创建一个字符串变量myString
	char[] myCharArray; //创建一个字符数组myCharArray
	myCharArray = myString.toCharArray(); //将字符串变量转换为字符数组
	for(int i=0; i<myCharArray.length; i++) {
	    System.out.print(myCharArray[i] + " "); //将每个字符打印出来
	}
	#h e l l o 1 2 3 

字符数组转成字符串

char[] str = {'h','e', 'l', 'l', 'o', '  ', '1','2','3'};  //创建一个字符数组
	String string1 = new String(str);
	String string2 = String.valueOf(str);
	System.out.println(string1);  //hello 123
	System.out.println(string2);

在上面的代码中,可以看出char数组转成字符串的方法有两种:

一种是直接将字符数组作为参数构造String对象; 另一种是使用String的valueOf()方法。

System.out.println(string1 == string2);  //false
	System.out.println(string1.equals(string2));  //true

两者的结果不一样,因为在string1 == string2中,比较的是地址,由于string1 和 string2是两个不同的对象,string1是通过new方法创建的,string2是由valueOf()方法返回的对象,所以二者的地址不一样,等式的结果就是false。

getChars() 方法将字符从字符串复制到目标字符数组。

public void getChars(int srcBegin, int srcEnd, char[] dst,  int dstBegin)

参数

srcBegin -- 字符串中要复制的第一个字符的索引。srcEnd -- 字符串中要复制的最后一个字符之后的索引。

dst -- 目标数组。dstBegin -- 目标数组中的起始偏移量。

返回值

没有返回值,但会抛出 IndexOutOfBoundsException 异常。

public void getChars(int srcBegin,int srcEnd,char[]dst,int dstBegin)
    String str="This is a String";
    Char chr =new char[10];
    Str.getChars(5,12,chr,0); //chr=is a St
    subString()方法是提取字符串的另一种方法,它可以指定从何处开始提取字符串以及何处结束。

字节数组 > 字符串

String(byte[]) :通过使用平台的默认字符集解码指定的 byte 数组,构
造一个新的 StringString(byte[]int offset ,int length) : :用指定的字节数组的一部分,
即从数组起始位置offset开始取length个字节构造一个字符串对象。

字符串 > 字节数组

public byte[] getBytes() :使用平台的默认字符集将此 String 编码为
byte 序列,并将结果存储到一个新的 byte 数组中。
public byte[] getBytes(String charsetName) : :使用指定的字符集将
此 String 编码到 byte 序列,并将结果存储到新的 byte 数组。

StringBuffer类

StringBuffer 类不同于String ,其对象必须使用构造器生成。有 三 个 构造 器 :
StringBuffer() :初始为 容量为16 的字符串缓冲区
StringBuffer(int size) :构造 指定容量的字符串缓冲区
StringBuffer(String str) :将内容初始化为指定字符串内容
String s = new String("我喜欢学习");
StringBuffer buffer = new StringBuffer("我喜欢学习");
buffer.append("数学")
StringBuffer append(xxx):提供了很多的append()方法,用于进行字符串拼接
StringBuffer delete(int start,int end):删除指定位置的内容
StringBuffer replace(int start, int end, String str):把[start,end)位置替换为str
StringBuffer insert(int offset, xxx):在指定位置
StringBuffer reverse() :把当前字符序列逆转
public int indexOf(String str)
public String substring(int start,int end)
public int length()
public char charAt(int n )
public void setCharAt(int n ,char ch)

StringBuilder

StringBuffer和StringBuilder构造方法和方法一样!

String,StringBuffer和StringBuilder

tringBuffer中的方法都有:synchronized 关键字修饰。表示StringBuffer在多线程环境下运行是安全的。StringBuilder中的方法都没有:synchronized 关键字修饰,表示StringBuilder在多线程环境下运行是不安全的。String类中有一个 byte[ ] 数组,这个byte[ ]数组采用了 final 修饰,因为数组一旦创建长度不可变。并且被final修饰的引用一旦指向某个对象之后,不可再指向其它对象,所以String是不可变的,StringBuffer和StringBuilder内部实际上是一个 byte[ ]数组,这个byte[]数组没有被final修饰,StringBuffer和StringBuilder的初始化容量是 16,当存满之后会进行扩容,底层调用了数组拷贝的方法System.arraycopy()。所以StringBuilder和StringBuffer适合于使用字符串的频繁拼接操作。

BigInteger

Integer类作为int的包装类,能存储的最大整型值为2 31 -1,Long类也是有限的,最大为2 63 -1,java.math包的BigInteger 可以表示不可变的任意精度的整数。

 常用 方法
 public BigInteger abs():返回此 BigInteger 的绝对值的 BigIntegerBigInteger add(BigInteger val) :返回其值为 (this + val)BigInteger
 BigInteger subtract(BigInteger val) :返回其值为 (this - val)BigInteger
 BigInteger multiply(BigInteger val) :返回其值为 (this * val)BigInteger
 BigInteger divide(BigInteger val) :返回其值为 (this / val)BigInteger。整数
相除只保留整数部分。
 BigInteger remainder(BigInteger val) :返回其值为 (this % val)BigIntegerBigInteger[] divideAndRemainder(BigInteger val):返回包含 (this / val) 后跟
(this % val) 的两个 BigInteger 的数组。
 BigInteger pow(int exponent) :返回其值为 (this exponent )BigInteger

BigDecimal类

一般的Float类和Double类可以用来做科学计算或工程计算,但在 商业计算中, 到 要求数字精度比较高,故用到java.math.BigDecimal类 类 。 BigDecimal类支持不可变的、任意精度的有符号十进制定点数。

 构造器
 public BigDecimal(double val)
 public BigDecimal(String val)
 常用方法
 public BigDecimal add(BigDecimal augend)
 public BigDecimal subtract(BigDecimal subtrahend)
 public BigDecimal multiply(BigDecimal multiplicand)
 public BigDecimal divide(BigDecimal divisor, int scale, int roundingMode)

时间类

Java 7

时间类的介绍与对比

类名称时间格式
java.util.Date(父类)年月日时分秒
java.sql.Date(子类)年月日
java.sql.Time(子类)时分秒
java.sql.Timestamp(子类)年月日时分秒毫秒
java.util.Calendar(日历类)年月日时分秒毫秒
java.text.DateFormat(格式化类)时间格式转换类

JDK8前

1. java.lang.System类 类
System类提供的public static long currentTimeMillis()用来返回当前时
间与197011000秒之间以毫秒为单位的时间差。
此方法适于计算时间差。
计算世界时间的主要标准有:
UTC(Coordinated Universal Time)
GMT(Greenwich Mean Time)
CST(Central Standard Time)
2. java.util.Date类 类
表示特定的瞬间,精确到毫秒
构造 器 :
Date(): :使用无参构造器创建的对象可以获取本地当前时间。
Date(long date)
常用方法
getTime():返回自 19701100:00:00 GMT 以来此 Date 对象
表示的毫秒数。
toString():把此 Date 对象转换为以下形式的 String: dow mon dd
hh:mm:ss zzz yyyy 其中: dow 是一周中的某一天 (Sun, Mon, Tue,
Wed, Thu, Fri, Sat),zzz是时间标准。
其它很多方法都过时了。

实例

mport java.util.Date;
Date date = new Date();
System.out.println(date);
System.out.println(System.currentTimeMillis());
System.out.println(date.getTime());
Date date1 = new Date(date.getTime());
System.out.println(date1.getTime());
System.out.println(date1.toString());
3. java.text.SimpleDateFormatDate类的API不易于国际化,大部分被废弃了,java.text.SimpleDateFormat
类是一个不与语言环境有关的方式来格式化和解析日期的具体类。
它允许进行 格式化:日期  文本、 解析:文本  日期
格式化:
SimpleDateFormat() :默认的模式和语言环境创建对象
public SimpleDateFormat(String pattern) :该构造方法可以用参数pattern
指定的格式创建一个对象,该对象调用:
public String format(Date date) :方法格式化时间对象date
解析:
public Date parse(String source): :从给定字符串的开始解析文本,以生成
一个日期
32
32
Date date = new Date(); // 产生一个Date实例
// 产生一个formater格式化的实例
SimpleDateFormat formater = new SimpleDateFormat();
System.out.println(formater.format(date));// 打印输出默认的格式
SimpleDateFormat formater2 = new SimpleDateFormat("yyyy年MM月dd日 EEE
HH:mm:ss");
System.out.println(formater2.format(date));
try {
// 实例化一个指定的格式对象
Date date2 = formater2.parse("2008年08月08日 星期一 08:08:08");
// 将指定的日期解析后格式化按指定的格式输出
System.out.println(date2.toString());
} catch (ParseException e) {
e.printStackTrace();
}
4. java.util.Calendar( 日历)类 类
Calendar是一个抽象基类,主用用于完成日期字段之间相互操作的功能。
获取Calendar实例的方法
使用Calendar.getInstance()方法
调用它的子类GregorianCalendar的构造器。
一个Calendar的实例是系统时间的抽象表示,通过get(int field)方法来取得想
要的时间信息。比如YEARMONTHDAY_OF_WEEKHOUR_OF_DAYMINUTESECOND
public void set(int field,int value)
public void add(int field,int amount)
public final Date getTime()
public final void setTime(Date date)
注意:
获取月份时:一月是0,二月是1,以此类推,12月是11
获取星期时:周日是1,周二是2 , 。。。。周六是7
alendar calendar = Calendar.getInstance();
// 从一个 Calendar 对象中获取 Date 对象
Date date = calendar.getTime();
// 使用给定的 Date 设置此 Calendar 的时间
date = new Date(234234235235L);
calendar.setTime(date);
calendar.set(Calendar.DAY_OF_MONTH, 8);
System.out.println("当前时间日设置为8后,时间是:" + calendar.getTime());
calendar.add(Calendar.HOUR, 2);
System.out.println("当前时间加2小时后,时间是:" + calendar.getTime());
calendar.add(Calendar.MONTH, -2);
System.out.println("当前日期减2个月后,时间是:" + calendar.getTime());

JDK8中

java.time 中包含了所有关于本地日期(LocalDate)、本地时间(LocalTime)、本地日期时间(LocalDateTime)、时区(ZonedDateTime)和持续时间(Duration)的类。历史悠久的 Date 类新增了 toInstant() 方法,用于把 Date 转换成新的表示形式。这些新增的本地化时间日期 API 大大简 化了日期时间和本地化的管理。

java.time – 包含值对象的基础包
java.time.chrono – 提供对不同的日历系统的访问
java.time.format – 格式化和解析时间和日期
java.time.temporal – 包括底层框架和扩展特性
java.time.zone – 包含时区支持的类
LocalDateLocalTimeLocalDateTime 类是其中较重要的几个类,它们的实例
是不可变的对象,分别表示使用 ISO-8601日历系统的日期、时间、日期和时间。
它们提供了简单的本地日期或时间,并不包含当前的时间信息,也不包含与时区
相关的信息。
LocalDate代表IOS格式(yyyy-MM-dd)的日期,可以存储 生日、纪念日等日期。
LocalTime表示一个时间,而不是日期。
LocalDateTime是用来表示日期和时间的,这是一个最常用的类之一。

注:ISO-8601日历系统是国际标准化组织制定的现代公民的日期和时间的表示法,也就是公历

33
33

瞬时:Instant

Instant:时间线上的一个瞬时点。 这可能被用来记录应用程序中的事件时间戳。 在处理时间和日期的时候,我们通常会想到年,月,日,时,分,秒。然而,这只是时间的一个模型,是面向人类的。第二种通用模型是面向机器的,或者说是连续的。在此模型中,时间线中的一个点表示为一个很大的数,这有利于计算机处理。在UNIX中,这个数从1970年开始,以秒为的单位;同样的,在Java中,也是从1970年开始,但以毫秒为单位。 java.time包通过值类型Instant提供机器视图,不提供处理人类意义上的时间单位。Instant表示时间线上的一点,而不需要任何上下文信息,例如,时区概念上讲,它只是简单的表示自1970年1月1日0时0分0秒(UTC)开始的秒数。因为java.time包是基于纳秒计算的,所以Instant的精度可以达到纳秒级。 (1 ns = 10 -9 s) 1秒 = 1000毫秒 =106微秒=109纳秒

34
34

时间戳是指格林威治时间1970 年01 月01 日00 时00 分00 秒( 北京时间1970 年01 月01日 日08 时00 分00 秒)

格式化与解析日期 或 时间java.time.format.DateTimeFormatter 类:该类提供了三种格式化方法: 预定义的标准格式。如: ISO_LOCAL_DATE_TIME;ISO_LOCAL_DATE;ISO_LOCAL_TIME 本地化相关的格式。如:ofLocalizedDateTime(FormatStyle.LONG) 自定义的格式。如:ofPattern(“yyyy-MM-dd hh:mm:ss”)

方 法 描 描 述
ofPattern(String pattern)静 态 方 法 , 返 回 一 个 指 定 字 符 串 格 式 的DateTimeFormatter
format(TemporalAccessor t) 格式化一个日期、时间,返回字符串
parse(CharSequence text) 将指定格式的字符序列解析为一个日期、时间
ZoneId :该类中包含了所有的时区信息,一个时区的ID,如 Europe/Paris
ZonedDateTime :一个在ISO-8601日历系统时区的日期时间,如 2007-12-03T10:15:30+01:00 Europe/Paris。其中每个时区都对应着ID,地区ID都为“{区域}/{城市}”的格式,例如:Asia/ShanghaiClock: :使用时区提供对当前即时、日期和时间的访问的时钟。
持续时间:Duration,用于计算两个“时间”间隔
日期间隔:Period,用于计算两个“日期”间隔
TemporalAdjuster : 时间校正器。有时我们可能需要获取例如:将日期调整到“下一个工作日”等操作。
TemporalAdjusters : 该类通过静态方法(firstDayOfXxx()/lastDayOfXxx()/nextXxx())提供了大量的常用TemporalAdjuster 的实现。
//ZoneId:类中包含了所有的时区信息
// ZoneId的getAvailableZoneIds():获取所有的ZoneId
Set<String> zoneIds = ZoneId.getAvailableZoneIds();
for (String s : zoneIds) {
System.out.println(s);
}
// ZoneId的of():获取指定时区的时间
LocalDateTime localDateTime = LocalDateTime.now(ZoneId.of("Asia/Tokyo"));
System.out.println(localDateTime);
//ZonedDateTime:带时区的日期时间
// ZonedDateTime的now():获取本时区的ZonedDateTime对象
ZonedDateTime zonedDateTime = ZonedDateTime.now();
System.out.println(zonedDateTime);
// ZonedDateTime的now(ZoneId id):获取指定时区的ZonedDateTime对象
ZonedDateTime zonedDateTime1 = ZonedDateTime.now(ZoneId.of("Asia/Tokyo"));
System.out.println(zonedDateTime1);
/Duration:用于计算两个“时间”间隔,以秒和纳秒为基准
LocalTime localTime = LocalTime.now();
LocalTime localTime1 = LocalTime.of(15, 23, 32);
//between():静态方法,返回Duration对象,表示两个时间的间隔
Duration duration = Duration.between(localTime1, localTime);
System.out.println(duration);
System.out.println(duration.getSeconds());
System.out.println(duration.getNano());
LocalDateTime localDateTime = LocalDateTime.of(2016, 6, 12, 15, 23, 32);
LocalDateTime localDateTime1 = LocalDateTime.of(2017, 6, 12, 15, 23, 32);
Duration duration1 = Duration.between(localDateTime1, localDateTime);
System.out.println(duration1.toDays());
/Period:用于计算两个“日期”间隔,以年、月、日衡量
LocalDate localDate = LocalDate.now();
LocalDate localDate1 = LocalDate.of(2028, 3, 18);
Period period = Period.between(localDate, localDate1);
System.out.println(period);
System.out.println(period.getYears());
System.out.println(period.getMonths());
System.out.println(period.getDays());
Period period1 = period.withYears(2);
System.out.println(period1);
// TemporalAdjuster:时间校正器
// 获取当前日期的下一个周日是哪天?
TemporalAdjuster temporalAdjuster = TemporalAdjusters.next(DayOfWeek.SUNDAY);
LocalDateTime localDateTime = LocalDateTime.now().with(temporalAdjuster);
System.out.println(localDateTime);
// 获取下一个工作日是哪天?
LocalDate localDate = LocalDate.now().with(new TemporalAdjuster() {
@Override
public Temporal adjustInto(Temporal temporal) {
LocalDate date = (LocalDate) temporal;
if (date.getDayOfWeek().equals(DayOfWeek.FRIDAY)) {
return date.plusDays(3);
} else if (date.getDayOfWeek().equals(DayOfWeek.SATURDAY)) {
return date.plusDays(2);
} else {
return date.plusDays(1);
}
}
});
System.out.println("下一个工作日是:" + localDate);
35
35

Math类常用方法

ath类的常用方法:
  1. abs绝对值
  2. acos,asin,atan,cos,sin,tan 三角函数
  3. sqrt 平方根
  4. pow(double a, double b) a的b次幂
  5. max(double a, double b) 取大值
  6. min(double a, double b) 取小值
  7. ceil(double a) 大于a的最小整数
  8. floor(double a) 小于a的最大整数
  9. random() 返回0.01.0的随机数
  10. long round(double a) double型的数据a转换为long(四舍五入)
  11. toDegrees(double angrad) 弧度->角度
  12. toRadians(double angdeg) 角度->弧度
public class TestMath {
    public static void main(String[] args) {
        //取整相关操作
        System.out.println(Math.ceil(3.2));
        System.out.println(Math.floor(3.2));
        System.out.println(Math.round(3.2));
        System.out.println(Math.round(3.8));
        //绝对值、开方、a的b次幂等操作
        System.out.println(Math.abs(-45));
        System.out.println(Math.sqrt(64));
        System.out.println(Math.pow(5, 2));
        System.out.println(Math.pow(2, 5));
        //Math类中常用的常量
        System.out.println (Math.PI);
        System.out.println(Math.E);
        //随机数
        System.out.println(Math.random()); //[0, 1)
    }
}
输出:
  4.0
  3.0
  3
  4
  45
  8.0
  25.0
  32.0
  3.141592653589793
  2.718281828459045
  0.19363107489981546