Java教程

实战Java程序设计易忘知识点

本文主要是介绍实战Java程序设计易忘知识点,对大家解决编程问题具有一定的参考价值,需要的程序猿们随着小编来一起学习吧!

一、数据类型和运算符

1.1 成员变量(实例变量)的默认初始值

数据类型初始值
int0
char‘\u0000’
double0.0
booleanfalse

1.2 基本数据类型

  • 数值型:byte(1)、short(2)、int(4)、long(8)、float(4)、double(8)
  • 字符型:char(2)
  • 布尔型:boolean(1)

1.3 二元运算符(+、-、*、/、%)运行规则

整数运算

  • 如果两个操作数有一个是long型,那么结果也是long型
  • 如果没有long型,结果为int,即使操作数全为short、byte,结果也是int

浮点运算

  • 如果两个操作数有一个是double,那么结果就是double
  • 只有两个操作数都是float,结果才是float

取模运算

  • 其操作数可为浮点数,一般用于整数,结果为整数

1.4 位运算符

运算符说明
~取反
&按位与
|按位或
^按位异或
<<左移运算符,相当于除以2
>>右移运算符,相当于乘以2

1.5 运算符优先级问题

在这里插入图片描述

二、控制语句

2.1 switch语句中的表达式数据类型

switch结构中的表达式,只能是以下六种数据类型之一:
byte、short、char、int、枚举类型、String
无论哪个版本的JDK,都是不支持 long,float,double,boolean 这个一定要注意!

2.2 循环结构的分类

  • 当型:先判断条件(布尔表示式)是否成立再去执行语句,例如while和for循环两个结构
  • 直到型:先执行语句,在判断布尔表示式,例如:do-while结构,另外注意do-while的结构,分号
do {
	循环体
}while(布尔表达式);

2.3 break语句和continue语句

  • break语句用于强行退出循环,不执行循环中剩余的语句
  • continue语句用在循环语句体中,用于终止某次循环过程,即跳过循环体中尚未执行的语句,接着进行下一次是否执行循环的判断。
  • continue语句用在while、do-while循环中时,continue立刻提到循环首部。continue语句用在for循环中时,跳到for循环的迭代因子部分。

2.4 语句块

2.4.1 格式

{
	代码块
}

2.4.2 注意

  • 作为一个整体,是要内被一起执行的。
  • 语句块可以被嵌套在另一个语句快中,但是不能在两个嵌套的语句内声明同名的变量
  • 语句块可以使用外部的变量,而外部不能使用语句块中定义的变量,语句块中的变量作用于仅限于语句块

2.5 方法的重载、

方法的重载是指一个类中可以方法名相同,但参数不同的方法。调用时,会根据不同的参数自动匹配对应的方法。

2.5.1 构成方法重载的条件

  • 不同的含义:形参类型、形参个数、形参顺序
  • 返回值不同的不构成方法的重载

2.6 递归结构

2.6.1 递归结构的组成

  • 定义递归头:解答“什么时候不调用自身方法”–>就是递归的结束条件。

  • 递归体:解答“什么是否需要调用自身方法”

  • 例子:递归求n!

    static long factorial(int i){
    	if(n == 1){
    		return 1;//递归头
    	}else{
    		return n*factorial(n-1);//递归体
    	}
    }
    

2.6.2 递归的评价

简单是递归的优点之一。但是递归会占用大量的系统堆栈,内存耗用多,在递归调用层多时速度比循环慢得多。
任何使用递归解决的问题也能用迭代解决,在高性能要求下,尽量避免使用递归转而使用迭代

三、Java面向对象编程基础

3.1 面向对象和面向过程思想

3.1.1概念

面向对象和面向过程都是对软件分析,设计和开发的一种思想,两者都贯穿于软件分析、设计和开发的各个阶段,对应面向对象就分别称为面向对象的分析(OOA)、面向对象的设计(OOD)和面向对象的编程(OOP)。

3.1.2 面向对象和面向过程的特点

  • 面向过程:当我们用面向过程的思想思考问题时,首先要思考“怎么按步骤实现?”,并将步骤对应成方法,一步一步最终完成,它适合简单任务,不需要过多协作的情况
  • 面向对象:面向对象跟契合人的思维模式。人们首先思考的是“怎么设计这个事物?”例如思考造车,就先思考“车怎么设计?”而不是“怎样按步骤造车”的问题。
  • 面向对象有三大特征:封装、继承、多态,而面向过程只有封装。
  • 面向对象可以帮助人们从宏观上把握,从整体上分析整个系统。但是,具体到部分微观操作的(就是一个个方法)实现的,仍然需要用面向过程的思路去实现的。

3.1.3 面向对象的思考方式

在遇到复杂的问题时,先从问题中找到名词,然后确定这些名词哪些可以作为类,在根据问题需求来确定类的属性和方法,最后确定类之间的关系。

3.2对象和类的概念

  • 对象是具体的事物;类是对对象的抽象
  • 类可以看成一类对象的模板,对象可以看做该类的一个具体实例
  • 类是用于描述同一类型对象的一个抽象概念,类定义了这一类对象所应具有的公共属性和方法

3.2.1 对象属性的默认值及作用域

属性的作用是这个类体。定义成员变量时,可以对其进行初始化,如果不对其初始化,Java将使用默认值初始化

数据类型默认值
整型0
浮点型0.0
字符型‘\u0000’
布尔型false
引用型null

3.3 对象的内存分析

3.3.1 Java虚拟机的内存模型

在这里插入图片描述
栈的特点如下:

  • 栈是方法执行的内存模型。每个方法调用时都会创建一个栈帧(存储变量、操作数、方法出口等)
  • JVM为每个线程都创建一个栈(线程私有),用于存放该线程执行方法的信息(实际参数、局部变量)
  • 栈的存储特性是“先进后出,后进先出”,是一个连续的内存空间,运算速度快

堆的特点如下:

  • 堆用于存储创建好的对象和数组(数组也是对象)
  • JVM只有一个堆,线程共有
  • 堆是一个不连续的内存空间,分灵活,但运算速度快

方法区的特点如下:

  • JVM只有一个方法区。线程共有
  • 方法区实际上也是堆,只是专门来存储类、常量的相关信息
  • 用来存放程序中永远不变或唯一的内容,如类信息对象(class对象)、静态变量、字符串常量等

3.4 构造器

3.4.1 构造器的误区

  • java通过new关键字调用构造器
  • 构造器虽然有返回值,但是不能定义返回值类型(返回值类型肯定时本类),不能再构造器中使用return返回某个值
  • 没有定义构造器,系统会自动定义无参构造器,否则不自动添加
  • 如果构造器的形参和类的属性名相同,需要用this关键字进行区分
  • 对象的创建并不是完全有构造器负责创建的

3.5 垃圾回收机制

3.5.1 容易造成内存泄露的操作

  • 创建大量无用对象
  • 静态集合类的使用
  • 各种连接对象(I/O流对象,数据库连接对象,网络连接对象)为关闭‘
  • 监听器的使用

3.6 参数传值机制

Java的方法中,所有参数都是“值传递”,传递的是值的副本

  • 基本数据类型参数的传值:传递的是值的副本,副本改变不会影响“原件”
  • 应用数据类型的参数:传递的也是值的副本,但是引用类型指的是“对象的地址”,因此,副本和原参数都是指向了同一个地址,改变“副本指向地址对象的值”,也就意味着原参数指向对象的值发生了改变

3.7 JDK中常用的包

包名说明
java.lang包含一些语言的核心类,如String、Math、Integer、System和Thread,提供常用的功能
java.awt包含了构成抽象窗口工具集(abstract window tooklkits)的多个类,这些类被用来构建和管理应用程序的图形用户界面
java.net包含执行与网络相关操作的类
java.io包含提供多种输入输出的类
java.util使用的工具类,如系统特性,使用与日期,日历相关的函数等

第五章、Java面向对象编程进阶

5.1、继承的使用

  • 对象 instanceof 类:判断对象是否是类的实例
  • 子类获得父类的全部属性和方法,除了父类构造器(但能通过super访问父类构造器)

5.1.1 子类重写父类方法的条件

  • “==”:方法名和参数列表相同
  • “<=”:返回值类型和声明异常类型,子类小于等于父类
  • “>=”:访问权限,子类大于等于父类。private -> default -> protect -> public
  • 方法的重写是实现多态的必要条件

5.1.2 继承树追溯

  • 属性和方法:从当前类不断向上找属性或方法,直到找到为止,找不到编译出错。
  • 构造器的执行:从最顶级的父类构造器执行到当前类构造器
  • 对象的创建->代码块、静态代码块、构造器的执行顺序:
    父类静态初始化块–>子类静态初始化块–>父类初始化块–>子类初始化块–>父类构造方法–>子类构造方法
    ##@# 5.2 hashcode的作用
    hashcode代表对象的地址说的是对象在hash表中的位置,物理地址说的对象存放在内存中的地址。HashCode的存在主要是为了查找的快捷性,HashCode是用来在散列存储结构中确定对象的存储地址的(后半句说的用hashcode来代表对象就是在hash表中的位置)

5.2 equals方法和==

  • “==”比较双方是否相同。
    • 如果是基本数据类型则表示值相等
    • 如果是引用数据类型,则代表地址(hashcode)相等,即代表同一对象
  • equals()方法定义为“对象的内容相等”。
    • Object的equals方法默认比较两个对象的hashcode,即是同一个对象的引用时返回true,否则返回false。
    • JDK提供了一些类,比如String、Date、包装类,重写了Object的equals方法,使用这些类的equals方法为x.equals(y),当x和y所引用的对象是同一类对象且属性内容相等时(并不一定是相同对象,都是通过new出来便是不同对象)返回true,否则返回false。
    • 一个对象可以有多个引用

5.3 super,final关键字

  • super作用:通过super关键字可以访问被子类覆盖的父类中的属性和方法,在子类构造器中,super关键字总是第一位,所以当执行构造器时,先执行父类构造器。
  • final作用
    • 修饰变量:一旦赋予初值,无法改变
    • 修饰方法:该方法不可被子类重写,但是可以被重载
    • 修饰类:修饰的类不能被继承,比如Math、String类

5.4 封装

5.4.1 封装的优点

  • 提高代码的安全性
  • 提高代码的复用性
  • 高内聚:封装细节,便于修改内部代码,提高可维护性
  • 低耦合:简化外部调用,便于使用者使用,便于扩展和协作

5.4.2 封装的实现-访问控制符

修饰符同一个类同一个包子类所有类
private
default
protect
public

5.4.3 封装的使用细节

类的属性的处理如下

  • 一般使用private访问权限
  • 提供相应的get/set方法来访问属性,这些方法通常是public修饰的,以提供对属性的赋值和读取操作。boolean变量的get方法是is开头的,比如 public boolean isFlag()
  • 只用于本类的辅助性方法可以用private修饰,希望其他类调用的方法用public修饰。

5.5 多态(统一方法+同一父类的不同子类=不同行为)

5.5.1多态的注意事项

  • 多态是方法的多态不是属性的多态
  • 多态存在的条件:继承,方法重写,父类引用指向子类对象

5.6 对象的转型

  • 向上转型:父类引用指向子类对象,引用变量只能调用编译类型的方法,也就是只能调用父类和子类中共同拥有的方法和属性,否则将会抛出异常。这个属于自动类型转换
  • 向下转换:将引用变量转换为运行时类型

5.7 抽象类的使用要点

  • 有抽象方法一定是抽象类
  • 抽象类不能实例化
  • 抽象类可以包含属性、方法和构造器,但不能通过构造器实例化,只能用来被子类调用
  • 抽象类只能被用来继承
  • 抽象方法子类必须实现

5.8 接口的定义说明

接口定义格式:[访问修饰符] interface 接口名 [extends 父接口1,父接口2…]

  • 访问修饰符:只能是public 或默认设置
  • 接口名:采用和类名相同的命名机制
  • extends:接口可以多继承
  • 常量:接口中的属性只能是常量,总是以public static final修饰,不写也是
  • 方法:接口中的方法只能是public abstract ,即使省略也是public abstract

5.8.1 接口的注意事项

  • 接口不能创建实例,可用于声明引用变量类型
  • JDK1.7之前,接口中只能包含静态常量和抽象方法,不能有构造器、普通方法和普通属性
  • JDK1.8之后,接口中包含普通的静态方法

5.8.2 面向接口编程

 面向过程编程(Procedure Oriented、简称PO) 和 面向对象编程(Object Oriented、简称OO) 我们一定听过,然而实际企业级开发里受用更多的一种编程思想那就是:面向接口编程(Interface-Oriented)!
 接口这个概念我们一定不陌生,实际生活中最常见的例子就是:插座!
 我们只需要事先定义好插座的接口标准,各大插座厂商只要按这个接口标准生产,管你什么牌子、内部什么电路结构,这些均和用户无关,用户拿来就可以用;而且即使插座坏了,只要换一个符合接口标准的新插座,一切照样工作!

5.9 内部类

5.9.1 内部类的作用

  • 内部类提供了更好的封装,只能由外部类通过创建内部类对象访问,

5.9.2 内部类的分类

  1. 成员内部类
    • 静态内部类:外部类可以通过静态内部类.名字的方式访问静态内部类的静态成员
    • 非静态内部类:内部类可以访问外部类所有方法和属性,但反过来不行
  2. 匿名内部类:格式:new 父类构造器(参数)\接口(),无构造方法
  3. 局部内部类:作用于仅限于方法内部

5.10 String类

5.10.1 常量池

  • 符号引用和直接引用的概念
    在java中,一个java类将会编译成一个class文件。在编译时,java类并不知道引用类的实际内存地址,因此只能使用符号引用来代替。比如org.simple.People类引用org.simple.Tool类,在编译时People类并不知道Tool类的实际内存地址,因此只能使用符号org.simple.Tool(假设)来表示Tool类的地址。而在类装载器装载People类时,此时可以通过虚拟机获取Tool类 的实际内存地址,因此便可以既将符号org.simple.Tool替换为Tool类的实际内存地址,及直接引用地址。
  • 全局字符串常量池(String Pool)
    全局字符串常量池中存放的内容是在类加载完成后存到String Pool中的,在每个VM中只有一份,存放的是字符串常量的引用值(在堆中生成字符串对象实例)
  • class文件常量池(Class Constant Pool)
    class常量池是在编译时每个class都有的,在编译阶段,它存放的是常量(文本字符串、final常量等)和符号引用
  • 运行时常量池(Runtime Constant Pool)
    运行时常量池是在类加载完成后,将每个class常量池中的符号引用转存到运行时常量池中。也就是说,每个class都会有一个运行时常量池,类在解析之后,将符号引用替换成直接引用,与全局常量池中的引用保持一致。

5.10.2 String类的常用方法

在这里插入图片描述

六、异常机制

6.1 异常的概念

异常(Exception)是指程序运行过程中出现的非正常现象,例如用户输入错误、除数为了0、需要处理的文件不存在、数组下标越界等

6.2 异常类的概念

在Java的异常处理机制中,引进了很多用来描述和处理异常的类,成为异常类。异常类定义中包含了该类异常的信息(异常的是什么)和对异常进行处理的方法

6.3 什么是异常处理

异常处理就是指程序在出现问题时依然可以正确执行完

6.4 Java如何处理异常

Java是采用面向对象的方式处理异常的,在处理过程中:

  1. 抛出异常:指在执行一个方式时,如果发生异常,则这个方法生成代表该异常的一个对象,异常对象中包含了异常类型和异常出现时的程序状态等异常信息,停止当前执行路径,并把异常对象提交给JRE
  2. 捕获异常:在方法抛出异常之后,运行时系统将转为寻找合适的异常处理器(exception handler)。潜在的异常处理器是异常发生时依次存留在调用栈中的方法的集合。当异常处理器所能处理的异常类型与方法抛出的异常类型相符时,即为合适的异常处理器。运行时系统从发生异常的方法开始,依次回查调用栈中的方法,直至找到含有合适异常处理器的方法并执行。当运行时系统遍历调用栈而未找到合适 的异常处理器,则运行时系统终止。

6.5 异常的分类

  1. error
  2. Exception
    1. 运行时异常:此类异常是由编程错误(程序员)引起的,比如空指针异常、数组下标越界、类型转换异常,其产生比较频繁,因此是由系统自动检测并将它们交给缺省的异常处理程序(用户可不必对其处理),用户通常通过增加“逻辑处理来避免这些异常”
    2. 可检查异常:所有不是运行时异常的异常都是可检查异常(不是由编程引起,文件找不到,没有找到该数据库表等等),比如IOException、SQLException等,这类异常在编译时就必须做出处理,否则无法通过编译。

6.6 不同异常的处理态度

对于运行时异常、错误或可查异常,Java技术所要求的异常处理方式有所不同。

  • 运行时异常: 由于运行时异常的不可查性,为了更合理、更容易地实现应用程序,Java规定,运行时异常将由Java运行时系统自动抛出,允许应用程序忽略运行时异常。

  • error:对于方法运行中可能出现的Error,当运行方法不欲捕捉时,Java允许该方法不做任何抛出声明。因为,大多数Error异常属于永远不能被允许发生的状况,也属于合理的应用程序不该捕捉的异常。

  • 可检查异常:对于所有的可查异常,Java规定:一个方法必须捕捉,或者声明抛出方法之外。也就是说,当一个方法选择不捕捉可查异常时,它必须声明将抛出异常。

能够捕捉异常的方法,需要提供相符类型的异常处理器。所捕捉的异常,可能是由于自身语句所引发并抛出的异常,也可能是由某个调用的方法或者Java运行时 系统等抛出的异常。也就是说,一个方法所能捕捉的异常,一定是Java代码在某处所抛出的异常。简单地说,异常总是先被抛出,后被捕的。

6.7 异常处理的方式

  1. 捕获异常:try-catch-finally
  2. 抛出异常:throws(在不知道如何处理该异常时使用)

6.7.1 try-catch-finally语句块的执行过程

  1. 程序首先执行可能发生异常的try语句块。如果try语句没有出现异常,则执行finally语句块
  2. 如果try语句出现异常,则中断执行以下代码,并根据异常的类型跳到对应的catch语句执行处理;catch语句执行完成后,程序会继续执行finally语句块

注意事项:
1. 即使try和catch中有return语句,finally语句块也会执行,只是在执行finally后才执行return
2. 只有在执行finally前遇到System.exit(0),finally才会不执行

七、数组

7.1 数组的见解

数组变量是属于引用类型, 数组也可以看做对象,数组中的每个元素相当于该对象的成员变量。数组本身就是对象,由于java中的对象是在堆中存储的,因此数组 是在堆中存储的

7.2 数组的初始化方式

  1. 静态初始化,定义数组的同时就为数组元素分配空间并赋值,形如:int [ ] a = {1,2,3}
  2. 动态初始化,数组的定义域为数组元素分配空间不能够赋值的操作分开进行,形如:int [ ] a = new int[2],a[0] = 1,a[1] = 2
  3. 数组的默认初始化,数组是引用类型,它的元素相当于类的实例变量,因此数组一经分配空间,其中的每个元素也被按照实例变量相同的方式隐式初始化。比如
int a[ ] = new int[2]:默认值是0,0.
boolean[ ] b = new boolean[2],默认值是false,false
String [ ] s = new String[0],默认值是null,null 

7.3 java.util.Arrays类

JDK提供了java.util.Arrays类包含了常用的数组操作,比如排序、查找、填充、打印内容等

八、常用类

8.1 基本数据类型的包装类

8.1.1 包装类的作用

  • 方便涉及到对象的操作
  • 可用于基本数据类型、包装类、字符串之间的相互转换

8.1.2 包装类的空指针问题

Integer i = null ;
int j = i;

8.1.3 包装类的缓存问题

 整型、char型所对应的包装类在自动装箱时,对于-128-127之间的值会进行缓存处理,目的是提高效率。
 缓存处理的原理为:如果数组在-128-127(1字节),那么在类加载时就已经为该区间的每个数组创建了对象,并将这256个对象缓存到一个名为cache的数组中,每个自动装箱过程发生时(或者手动调用了valueOf方法),就会判断该数据是否在该空间,如果有则直接获取,如果不在则通过new创建
该赋值方式报出空指针异常问题。因为对象i没有指向任何对象

8.1.4 总结

  • 自动装箱调用的是valueOf方法,不是new Integer()
Integer i = Integer.ValueOf(100)
  • 自动拆箱调用的是xxxValue()方法
int j = i.intValue()

8.2 字符串相关类

String类、StringBuilder类、StringBuffer类是三个与字符串相关的类。String类对象代表不可变字符串序列,StringBuilder和StringBuffer代表可变字符串序列。

8.2.1 String

  • String类无法改变的原因:
private final char value[];

上面是字符串的底层源码,字符串的全部内容一次性存储到数组中,而数组的类型定义为final,所以无法改变字符串的值

8.2.2 StringBuild和StringBuffer

  • StringBuild和StringBuffer可改变的原因
char value[]

它们的底层源码显示,也是通过数组实现,但是这两个数组没有final修饰,所以可以改变。两者的区别如下:

  • StringBuffer:JDK1.0版本提供的类,线程安全,做线程同步检查,效率较低
  • StringBuilder:JDK1.5版本提供的类,线程不安全,不做线程同步检查,效率较高,建议采用

StringBuild和StringBuffer的常用方法

  • 重载的public StringBuilder append(…)方法,尾部添加字符串返回自身
  • public StringBuilder delete(int start,int end)删除start到end-1范围字符串,返回自身
  • public StringBuilder deleteCharAt(int index)删除指定位置的char,返回自身
  • 重载的public StringBuilder insert(…)指位置上插入字符串,返回自身
  • public StringBuilder reverse()将字符串逆序,返回自身
  • public String toString()返回数据中的字符串表示形式
  • 和String类含有相同类似的方法
    • public int indexOf(String str)
    • public int indexOf(String str,int fromIndex)
    • public String subString(int start)
    • public String subString(int start,int end)
    • public int length()
    • char charAt(int index)

8.3 时间处理相关类

时间是单向的并且是唯一的,所以需要一把刻度尺来表达和度量时间。在计算机世界,人们把1970年1月1日00:00:00定为基准时间。Java用long类型的变量来表示时间

long now = System.currentTimeMillis();
  • 构造方法
    Date() 分配 Date 对象并初始化此对象,以表示分配它的时间(精确到毫秒)。以本机时间为准。
    Date(long date) 分配 Date 对象并初始化此对象,以表示自从标准基准时间(称为“历元(epoch)”,即 1970 年 1 月 1 日 00:00:00 GMT)以来的指定毫秒数。例:Date date1=new Date(10000000000L); 指历元后的10000000000毫秒所表示的时间。
  • 常用方法
    boolean after(Date when) 测试此日期是否在指定日期之后。
    boolean before(Date when) 测试此日期是否在指定日期之前。
    int compareTo(Date anotherDate) 比较两个日期的顺序。 如果参数 Date 等于此 Date,则返回值 0;如果此 Date 在 Date 参数之前,则返回小于 0 的值;如果此 Date 在 Date 参数之后,则返回大于 0 的值。
    boolean equals(Object obj) 比较两个日期是否相等。
    long getTime() 返回自 1970 年 1 月 1 日 00:00:00 GMT 以来此 Date 对象表示的毫秒数。
    void setTime(long time) 设置此 Date 对象,以表示 1970 年 1 月 1 日 00:00:00 GMT 以后 time 毫秒的时间点。
    String toString() 把此 Date 对象转换为以下形式的 String: dow mon dd hh:mm:ss zzz yyyy 。

8.3.1 DateFormat和SimpleDateFormat 日期格式转换器

date输出的日期格式类型是默认类型,所以需要SimpleDateFormat 来自定义日期的格式。
默认格式:yy-MM-dd aK:m
在这里插入图片描述

8.3.2 Calendar日历类

Calendar日历类是一个抽象类,为人么提供了关于日期计算的相关功能,例如年、月、日、分、秒的展示和计算。GregorianCalendar是Calendar的一个具体子类,提供了世界上大多数国家或地区使用的标准日历系统在这里插入图片描述
在这里插入图片描述

8.3 Math类

java.lang.Math提供了一系列用于科学计算的静态方法,其方法的参数和返回值类型一般为double型。当需要更加强大的数学计算能力时,如计算高等数学中的相关内容,可以用Apache commons下面的Math类库

8.3.1 Math类的常用方法

  • 求最大值、最小值和绝对值
    在这里插入图片描述
  • 求整运算
    在这里插入图片描述
  • 三角函数运算
    在这里插入图片描述
  • 指数运算
    在这里插入图片描述

8.3.2 java.util.Random类的常用方法

  • protected int next(int bits):生成下一个伪随机数。boolean nextBoolean():返回下一个伪随机数,它是取自此随机数生成器序列的均匀分布的boolean值。
  • void nextBytes(byte[] bytes):生成随机字节并将其置于用户提供的 byte 数组中。
  • double nextDouble():返回下一个伪随机数,它是取自此随机数生成器序列的、在0.0和1.0之间均匀分布的 double值。
  • float nextFloat():返回下一个伪随机数,它是取自此随机数生成器序列的、在0.0和1.0之间均匀分布float值。
  • double nextGaussian():返回下一个伪随机数,它是取自此随机数生成器序列的、呈高斯(“正态”)分布的double值,其平均值是0.0标准差是1.0。
  • int nextInt():返回下一个伪随机数,它是此随机数生成器的序列中均匀分布的 int 值。
  • int nextInt(int n):返回一个伪随机数,它是取自此随机数生成器序列的、在(包括和指定值(不包括)之间均匀分布的int值。
  • long nextLong():返回下一个伪随机数,它是取自此随机数生成器序列的均匀分布的 long 值。
  • void setSeed(long seed):使用单个 long 种子设置此随机数生成器的种子

8.4 java.io.File类

File类来代表文件或目录。在开发中读取文件、生成文件、删除文件、修改文件的属性时经常会用到该类。

8.4.1 FIle类的基本用法

  • FIle类的常见构造器:public File(String pathname)
  • File类访问属性的方法
方法说明
public boolean exists()文件是否存在
public boolean isDerectory()是否是目录
public bookean isFile()是否是文件
public long lastModified()返回最后修改时间
public long length()返回文件大小
public String getName()返回文件名
public String getPath()返回文件的目录路径
  • File类创建文件或目录的方法
方法说明
createNewFile()创建新的File
delete()删除File对应的文件
mkdir()创建一个目录,中间某个目录缺失,创建则失败
mkdirs()创建多个目录,也会逐层创建缺失目录
  • 创建文件例子
    先得到一个FIle对象再用该对象调用方法
    File f = new File("a.txt");
    f.createNewFile();
    

九、容器(集合)

9.1 Collection接口的方法(List和Set共有方法)

  • size():返回集合中元素的个数
  • add(Object obj):向集合中添加一个元素
  • addAll(Colletion coll):将形参coll包含的所有元素添加到当前集合中
  • isEmpty():判断这个集合是否为空
  • clear():清空集合元素
  • contains(Object obj):判断集合中是否包含指定的obj元素
  • ① 判断的依据:根据元素所在类的equals()方法进行判断
  • ②明确:如果存入集合中的元素是自定义的类对象,要去:自定义类要重写equals()方法
  • constainsAll(Collection coll):判断当前集合中是否包含coll的所有元素
  • rentainAll(Collection coll):求当前集合与coll的共有集合,返回给当前集合
  • remove(Object obj):删除集合中obj元素,若删除成功,返回ture否则
  • removeAll(Collection coll):从当前集合中删除包含coll的元素
  • equals(Object obj):判断集合中的所有元素 是否相同
  • hashCod
    e():返回集合的哈希值
  • toArray(T[] a):将集合转化为数组
  • ①如有参数,返回数组的运行时类型与指定数组的运行时类型相同。
  • iterator():返回一个Iterator接口实现类的对象,进而实现集合的遍历。
  • 数组转换为集合:Arrays.asList(数组)

9.2 List接口

9.2.1 List接口的特点和常用方法

List是可重复有序的集合,除了包含Collection接口的方法,还增加了和顺序(索引)相关方法。List接口的实现类由ArrayList、LinkedList和Vector

方法说明
void add(int index,Object element)指定位置插入元素
Object set(int index,Object element)修改指定位置的元素
Object get(int index)获取指定位置的元素
Object remove(int index)删除指定位置的元素
int indexOf(Object o)返回第一个匹配的元素位置
int lastIndexOf(Object o)返回最后一个匹配元素的位置

9.2.2 ArrayList的特点和底层实现

ArrayList的底层是数组实现的,其特点是查询效率快,增删效率低,线程不安全。在List的多个实现类中一般使用数组来处理业务。

  • ArrayList的底层存储结构
    private transient Object[] elementData;
    
    从ArrayList的底层存储结构可以,长度没有固定设置final,所以可以存放任意数量的对象,而扩容的本质就是通过定义新的更大数组,并将旧数组中的内容赋值到新数组来实现扩容。ArrayList的Object数组初始化长度为10。

9.2.3 LinkedList的特点和底层实现

LinkedList底层用双向链表来实现存储,其特点是查询效率低,增删效率快,但线程不安全。双向链表也叫双链表,是链表的一种,它的每个数据节点中都有两个指针,分别指向前一个节点和后一个节点。

  • LinkedList的底层源码实现
    transient的作用:就是让某些被修饰的成员属性变量不被序列化
public class LinkedList<E>
	extends AbstractSequentialList<E>
	implements List<E>,Deque<E>,Cloneable,java.io.Serializable
	
private transient Entry<E> header = new Entry<E>(null,null,null);
private transient int size = 0;

public LinkedList(){
	header.next = header.previous = header;
}

//链表节点
private static Entry<E>{
	E element;
	Entry<E> next;
	Entry<E> previous;
	Entry(E element, Entry<E> next, Entry<E> previous){
		this.element = element;
		this.next = next;
		this.previous = previous;
		}
}

9.2.4 Vector向量

Vector底层是一个长度可以动态增长的对象数组,它的相关方法都进行了线程同步,因此线程安全效率低,例如,indexOf()方法增加了synchronized同步标记

public synchronized int indexOf(Object o,int index){}

9.2.5 如何选择Vector、ArrayList和LinkedList

  • 需要线程安全,用Vector
  • 不存在线程安全问题并且查找较多时,用ArrayList
  • 不存在线程安全问题并且增删较多,用LinkedList

9.3 Map接口

Map接口用于存储键值对结构信息。Map接口的实现类由HashMap、TreeMap、HashTable和Properties等。

9.3.1 Map接口的常用方法

方法说明
Object put (Object key,Object value)存放键值对
Object get(Object key)获取指定键的值对象
Object remove(Object key)删除指定的键值对
boolean containsValue(Object value)判断容器中是否包含键值对
int size()包含键值对的数量
boolean isEmpty()Map是否为空
void putAll(Map map)将map中的所有键值对存放到Map对象中
void clear()清空Map对象的所有键值对

9.3.2 HashMap和HashTable

HashMap采用散列算法来实现的,它是Map接口最常用的实现类。由于底层采用哈希表来存储数据,因此要求键不能重复,新的键值对会替换旧的键值对。HashMap在查找、删除、修改等方面效率高。

  1. HashMap和HashTable的区别
    HashMap:线程不安全,效率高。允许key或value为null
    HashTable:线程安全,效率低。不允许key或value为null
  2. HashMap底层实现详解
    HashMap底层实现采用了哈希表,这是一种非常重要的数据结构。哈希表的基本结构是“数组+链表”
public class HashMap<K,V>
	extends AbstractMap<K,V>
	implements Map<K,V>,Cloneable,Serializable{
	//核心数组默认初始化的大小为16(数组必须为2的整数幂)
	static final int DEFAULT_INTTIAL_CAPACIIY = 16;
	//负载因子(核心数组被占用0.75开始扩容)
	static final float DEFAULT_LOAD_FACTOR = 0.75f;
	transient Entry[] table;
	static class Entry<K,V> implements Map.Entry<K,V>{
		final K key;
		V value;
		final int hash;
		Entry(int hash,K k,V v,Entry<K,V> n){
			value =  v;//值对象
			key = k;//键对象
			next = n;//下一个节点
			hash = h;//键对象的hash值
			}
		}
	
	

十、多线程技术

10.1 多线程的基本概念

10.1.1 程序的概念

程序是一个静态概念,一般对于操系统中的一个可执行文件,比如手机软件,当我们双击进入酷狗的可执行文件,将会加载该程序到内存中执行它,由此就产生"进程(正在执行的程序)”

10.1.2 进程的概念

执行中的程序叫做“进程”,这是一个动态的概念,现代的操作系统都k可以同时启动多个进程。进程的特点如下:

  • 进程是程序的一次动态执行过程,占用特定的地址是空间
  • 每个进程由3部分组成:CPU、Data、Code。每个进程都是独立的,保有自己的CPU时间、代码和数据。即便用同一份程序产生好几个进程,它们都有这三样东西,这造成的缺点是浪费内存,CPU负担重
  • 多任务操作系统将CPU时间动态规划分别每个进程,操作系统同时执行多个进程,每个进程独立运行。

10.1.3 进程与线程的区别

  • 进程是动态的,有自己的生命周期,而程序只是指令的集合,是静态的,没有执行的含义。
  • 没有建立进程的程序不能作为1个独立单位得到操作系统的认可。
  • 1个程序可以对应多个进程甚至一个进程都没有,但1个进程只能对应1个程序

10.1.4 线程的概念及特性

一个进程产生多个线程。线程与多个进程可以共享操作系统的某些资源,同一个进程的多个线程也可以共享此进程的某些资源(例如代码和数据),所以线程又被称为轻量级线程。线程特点如下:

  • 一个进程内部的一个执行单元,它是程序中一个单一得到顺序控制流程
  • 一个进程可拥有多个并行的线程
  • 一个进程中的多个线程共享相同的内存单元/内存地址空间,可以访问相同的变量和对象,而且它们从一个堆中分配对象并进行通信、数据交换和同步操作。
  • 由于线程间的通信是在同一个地址空间进行的,所以不需要额外的通信机制
  • 线程的启动、中断和消亡所消耗的资源非常少

10.1.5 进程与线程的区别

  • 线程可以看做是轻量级的进程,属于同一个进程的线程共享代码和数据空间,每个线程有独立的运行栈和程序计数器,
  • 进程是资源分配的单位,线程是调度和执行的单位
  • 系统在运行的时候会为每一个进程分配不同的内存区域,但是不会为线程分配内存区域(线程所使用的资源是它所属的进程的资源),线程组只能共享资源,也就是说,线程处理在运行时占用CPU资源,其他资源只能通过共享进程来获取。

10.2 线程的状态和生命周期

线程创建之后,我么必须掌握线程生命周期知识以相关状态的切换方法。

10.2.1 线程状态

一个线程对象的生命周期需要经历5个状态,分别如下

  • 1、新生状态
    new出来的线程对象就是处于新生状态,处于新生状态的线程有自己的内存空间,此时还没有获得CPU的资源,通过调用start方法进入就绪状态。
  • 2、就绪状态
    就绪状态的线程已具备了两运行条件,但是还没有被分配到CPU,处于“线程就绪队列”,等待系统为其分配CP,一旦获取CPU,线程就进入运行状态并且自动调用run方法。线程进入就绪状态的方式:
    • 新建线程:调用start方法,进入就绪状态
    • 阻塞线程:阻塞解除不会立即获取CPU资源,所以进入就绪状态
    • 运行线程:调研yield方法,直接进入就绪状态
    • 运行线程:JVM将CPU资源从本线程切换到其他线程
  • 3、运行状态
    在运行状态的线程执行其run方法,直到因调用其他方法导致线程终止。或等待某资源产生阻塞或完成任务死亡。如果过在给定的时间片段内没有执行结束,线程便进入就绪状态
  • 4、阻塞状态
    阻塞状态是指暂停一个线程的执行以等待某个条件的发生(如需要获取某个资源)。如下原因会导入线程进入阻塞状态
    • 执行sleep(int time)方法,使当前线程睡眠进入阻塞状态,时间到便进入就绪状态
    • 执行wait()方法,使当前线程进入阻塞状态当使用notify()方法唤醒这个线程后,它进入就绪状态
    • 当线程执行时,某个操作进入阻塞状态,例如执行I/O流操作(read()和write()方法本身就是阻塞方法)。只有当引起该操作阻塞的原因消失后才进入就绪状态
    • join()线程联合:当某个线程等待另一个线程执行结束并能继续执行时,使用join()方法
  • 5、死亡状态
    线程生命周期的最后阶段,当线程进入该阶段后,就无法回到其他状态,进入死亡状态原因如下:
    • 线程执行完run()方法里面的全部工作
    • 线程被强制终止,如通过stop()或destroy()方法来终止程序

10.3 线程的常用方法和优先级

10.3.1 线程的常用方法

方法说明
isAlive()判断线程是否终止
getPriority()获得线程的优先级
setPriority()设置线程的优先级
setName()设置线程的名字0
getName()获取线程的名字
currentThread()获取当前线程

10.3.2 线程的优先级

处于就绪状态的线程,会进入“就绪队列”等待JVM来挑选。
线程的优先级用数字表示,范围从1-10,一个线程的默认优先级是5.,数字越大优先级越高

  • int getPriority():获取线程的优先级
  • void setPriority():设置线程的优先级

10.4 线程同步

在处理多线程问题时,如果多个线程同时访问同一个对象,并且某些线程还想修改这个对象时,就需要用到“线程同步”机制

10.4.1 线程同步的概念

线程同步其实就是一种等待机制,多个需要同时访问一个对象的线程进入这个对象的等待池线程队列,等待前面的线程使用完毕,下一给线程再使用。

10.4.2 实现线程同步

通过private关键字可以保障数据对象只能通过方法访问,所以只需要针对方法提出一套机制即可。这套机制就是使用synchronized关键字,它包括两种用法:synchronized方法和synchronized快

  • 案例1:多线程操作同一个对象(未使用线程同步)
public class Class_11_9 {
    public static void main(String[] args) {
        Account account = new Account(1000,"小白");
        Drawing drawing1 = new Drawing(100,account);//定义取钱线程对象
        Drawing drawing2 = new Drawing(200,account);//定义取钱线程对象
        Drawing drawing3 = new Drawing(300,account);//定义取钱线程对象
        drawing1.start();//儿子取钱
        drawing2.start();//女儿取钱
        drawing3.start();//老婆取钱
    }
}
/*简单的银行账户*/
class Account{
    int money;
    String name;

    public Account(int money,String name){
        this.money = money;
        this.name = name;
    }
}
/*模拟提款操作*/
class Drawing extends Thread{
    int drawingNum;//要取出多少钱
    Account account;//要取钱的账户
    int expenseTotal;//总共取的钱数

    public Drawing(int drawingNum,Account account){
        super();
        this.drawingNum = drawingNum;
        this.account = account;
    }

    @Override
    public void run() {
        if ((account.money - drawingNum) < 0){
            return;
        }
        try {
            Thread.sleep(2000);//判断完成后阻塞。其他线程开始执行
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        account.money = account.money - drawingNum;
        expenseTotal = expenseTotal+drawingNum;
        System.out.println(this.getName()+"--账户余额:"+account.money);
        System.out.println(this.getName()+"--共取出了:"+expenseTotal);
    }
}
  • 案例2:多线程操作同一个对象(使用线程同步)
这篇关于实战Java程序设计易忘知识点的文章就介绍到这儿,希望我们推荐的文章对大家有所帮助,也希望大家多多支持为之网!