1、JAVA体系基础
1.1、JAVA 体系
- 标准版 :Java SE (Java Standard Edition)
- 企业版 :Java EE (Java Enterprise Edition)
- 小型版 :Java ME (Java Micro Edition)
- 小程序 :Java Card (运行在小内存设备上的平台)
1.2、JDK下载地址
- JDK 下载地址 :https://www.oracle.com/cn/java/
- JDK 关系 :JDK > JRE > JVM
- JDK (软件开发工具包)— 编写程序
- JRE (软件运行环境) — 运行软件运行程序
- JVM (Java虚拟机) — 计算设备规范,通过实际计算机上仿真模拟各种计算机功能实现
- 常用编译工具 :Notepad++ 、Sublime
1.3、注释说明
- 注释分类 :单行注释、多行注释、多行(文档注释)
- 单行注释 :从 // 开始,到本行结束,都是注释
- 多行注释 :从/* 开始,到*/结束,中间所有都是注释
- 多行注释 :从/*开始,到/结束,是一种支持提取的注释
1.4、变量、常量
- 常量: 程序在运行期间不发生变化的量
- 变量: 程序在运行期间发生变化的量 ( 声明方式 : 数据类型 变量名 = 初始值)
class HelloWorld{
public static void main(String[] args){
// 定义一个变量,定义一个整数类型的变量,名字为age,它的值为10
int age = 10;
System.out.printIn(age);
// 修改age变量的值
age = 20;
System.out.println("age变化后的值是:" + age);
}
}
1.4.1、局部变量的使用
- 定义位置 :在方法的内部或语句块内
- 变量一定要赋初始值,否则在使用是会报错
- 变量名不能重复(在同一个作用域内)
- 栈区存放程序当中所有的局部变量
class Demo{
public static void main(String[] args){
// 定义byte类型
byte age = 127;
System.out.println(age);
}
public void method(){
int age = 18;
}
}
1.4.2、成员变量
- 数值类型 :默认值为 0
- boolean型 :false
- 引用类型 :null
public class Person{
String name;
int age;
// 成员方法 : 成员变量和成员方法属于类内部成员,可直接访问
void show(){
System.out.println("年龄"+ age )
}
void setName(String s){
name = s;
}
void setAge(int i){
age = i;
}
// 有返回值的函数
String getName(){
return name;
}
// 修改传递多参数
void setNameAge(String s,int i){
name = s;
age = i;
}
// 可变长参数(可看作一维数组)
void showArgument(String... args){
for(int i=0;i<args.length;i++){
System.out.println("第" + i + "个参数是" + args[i])
}
}
public static void main(String[] args){
// 类的实例化, 数据类型(类名) 引用变量名 = new 类名();
Person p = new Person();
p.name = "lcl";
p.age = 18;
// 方法的调用
p.show();
p.setName("lcl")
p.setAge(18)
p.setNameAge("lcl",18)
p.showArguement("参数1","参数2","参数3")
String myName = p.getName()
System.out.println("名字"+ name )
}
}
2、数据类型
2.1、基本数据类型
- 基本数据类型
- byte、shot、int、long、float、double、boolean、char
- 引用数据类型
- 数组、类、接口、枚举、标注
类型 | 占用存储空间 | 表示范围 |
---|---|---|
byte | 1字节=8bit | -128~127 |
short | 2字节 | -2(15次方)~2(15次方-1) |
int | 4字节 | -2(31次方)~ 2 (31次方-1) |
long | 8字节 | -2(63次方)~ 2(63次方-1) |
class Demo{
public static void main(String[] args){
// 定义byte类型
byte age = 127;
System.out.println(age);
// 定义short类型
short persnonCount = 1000;
System.out.println(persnonCount);
// 定义longth类型,定义的时候数值后面可以加1
long starCount = 100000001;
System.out.println(starCount);
}
}
2.2、数据类型转化
- 自动类型转换 : 自动类型转换主要指从小类型到大类型之间的转换
- byte —>short (char) —>int —>long —>float —>double
- 强制类型转化 : 强制类型转换主要指从大类型到小类型之间的转换
- 目标类型 变量名 = (目标类型)源类型变量名;
- double —>float —>long —>int —>short (char) —>byte
class Demo{
public static void main(String[] args){
int i = 10
// 数据类型强制转化,前提精度不会损失
byte age = (byte)i;
System.out.println(age);
}
}
3、运算符
- 算数运算符
- 赋值运算符
- 关系运算符
- 逻辑运算符
- 位运算符
- 三元运算符
3.1、算数运算符
class Demo{
public static void main(String[] args){
int a = 10
int b = 15
// + - * / 加减乘除运算符 % 取余数
int c = a + b;
int d = a - b;
int e = a * b;
// int 间 做除法是整数
int f = a / b;
System.out.println(c)
}
}
``
**变量叠加**
- ++ -- 给数值变量自身加 1 或 减1
```java
class Demo{
public static void main(String[] args){
// 后 ++ ( a = a + 1)
int a = 1;
a++ ;
System.out.println(a) //2
int b = 1
// 如果后 ++ 和使用这个后加加一起运算使用的时候,使用的是 +1 之前的值
System.out.println(b++) ; // 1
System.out.println(b); // 2
// 前 ++
int a = 1;
++a;
System.out.println(b); // 2
// 如果前 ++ 和使用这个后加加一起运算使用的时候,使用的是 +1 之后的值
System.out.println(++b) ; // 2
}
}
3.2、赋值运算符
class Demo1{
// =,+=,-=,*=,/=,%=
public static void main(String[] args){
int a = 10;
a += 10;
System.out.println(a);
short.out.println(a);
}
}
3.3、逻辑运算符
运算符 | 含义 |
---|---|
& | 与/and |
&& | 与/and |
竖竖 | 或/or |
竖竖 | 双或/or |
3.4、判断运算符
3.4.1、IF 分枝判断
class Demo1{
public static void main(String[] args){
int score = 90;
if (score >= 60){
System.out.println("恭喜你及格了");
}else if(score >= 80){
System.out.println("你打了个优秀");
}else if(score>=90){
System.out.println("你打了90多分");
}else{
System.out.println("你没及格");}
}
}
3.4.2、Switch 分枝判断
// 不要漏掉 break
public class demo02 {
public static void main(String[] args){
String grade = "F";
switch (grade){
case "A":
System.out.println("优秀");
break;
case "B":
System.out.println("良好");
break;
case "C":
System.out.println("及格");
break;
case "D":
System.out.println("不及格");
break;
default:
System.out.println("未匹配");
}
}
}
3.4.3、三元运算符
- 语法 :逻辑表达式2 ? 表达式3: 表达式4
class Demo1{
public static main(String[] args){
int gender = 2;
// 三元运算符、可以是常量、变量、表达式
char c = gender ==1?'男':'女';
System.out.pringln(c);
}
}
3.4.4、循环结构(while)
class Demo11{
public static void main(String[] args){
int i = 1
while(i<= 100){
System.out.println(i);
i ++;
}
System.out.println("结束")
}
}
3.4.5、do…while循环
- 语法:do { 循环体 } while(表达式)
class Demo12{
public static void main(String[] args){
int i = 1
do {
if (i % 2 == 0){
total += i;
}
i ++;
}while(i<= 100);
System.out.println("结束")
}
}
3.4.6、for 循环
- 语法 : for (表达式1;表达式2;表达式3) { 循环体}
- 表达式1 :计数器的初始化,它只初始化一次
- 表达式2 :循环条件的判断,多次执行
- 表达式3 :修改计数器,多次执行
class Demo12{
public static void main(String[] args){
int i = 1
for (int i = 1;i<= 1000;i++){
if( i == 5){
continue;
}
System.out.println(i);
}
}
}
3.4.7、案例代码
import java.util.Scanner;
public class demo05 {
public static void main(String[] args) {
// 从键盘输入不确定的整数,并判断正数和复数的个数
// 创建接收控制台输入的对象
Scanner scanner = new Scanner(System.in);
int i = 0,a = 0,b = 0;
do {
System.out.println("请输入一个整数");
i = scanner.nextInt();
if(i>0){a++;};
if(i<0){b++;};
}while (i!=0);
System.out.println("结束循环");
System.out.println("输入正数个数" + a);
System.out.println("输入负数个数" + b);
}
}
4、数 组
- 一维数组 :记录多个类型相同的数据内容,内存中申请一段连续的存储单元,元素按照线性排列
- 声明方式 1 ( 数组类型【】 数组名称 = new 数据类型【数组长度】)
- 声明方式2 (数据类型【】 数组名称 = {初始值1,初始值2,初始值3,…})
- length属性可以获取数组长度、下标从 0 开始
- 二维数组 :多个一维数组组成
- 声明方式 1 ( 数组类型【】【】 数组名称 = new 数据类型【行数】【列数】)
- 声明方式2 (数据类型【】 数组名称 = {{初始值1,初始值2,初始值3},…})
4.1、数组工具类
- java.util.Arrays类
- static String toString(int[] a) 输出数组内容
- static void fill(int[] a,int val) 将参数指定元素赋值给数组中的元素
- static bookean equals(boolean[] a,boolean[] a2) 判断两个数组元素内容和次序是否相同
- staic void sort(int[] a) 对数组中的元素进行从小到大的排序
- staic int binarySearch(int[] a,int key) 从数组中查找参数指定元素所在位置
5、面向对象编程
- 面向对象的特性 : 封装、继承、多态
- 类与对象 :类(class)、对象(object)
- 类是 对 一类事物描述,是抽象的 (属性 + 行为)
- 对象 是 实际存在的该类 事务的每个个体,因而称之为实例
- 堆区用于存放所有 new 产生的对象 和数组
5.1、类的基本语法
修饰符 class 类名{
属性声明; // 修饰符 类型 属性名=初值;
方法声明;
}
// 修饰符 public :类可以被任意访问
// 类的正文要用 {} 括起来
// 方法
修饰符 返回值类型 方法名(参数列表){
方法体语句;
}
// 修饰符 : public , private , protected
// 返回值类型 : return 语句传递返回值。没有返回值 :void
修饰符 | 类内部 | 同一个包 | 子类 | 任何地方 |
---|---|---|---|---|
private | yes | |||
空白 | yes | yes | ||
protected | yes | yes | yes | |
public | yes | yes | yes | yes |
5.2、函数的调用
class Demo1{
public static void main(String[] args){
int a = 10;
int b = 19;
// 调用写好的方法,使用返回值来接收方法的返回结果
int result = compareNum(a,b);
System.out.println(result+"大");
}
// 如果一个方法是 void 那么我们不需要返回值,所以不需要写 return 变量值
public static int compareNum(int a,int b){
int c = 0;
if (a > b){
c = a;
}else{
c = b ;
}
return c;
}
}
// public 公开类
public class Person{
// 成员变量 姓名-属性 - 公开属性
public String name;
// 成员变量 年龄 - 属性 - 私有属性
private int age;
// 工作 - 行为
public void work(){
System.out.println("正在工作...");
}
// 唱歌 - 行为
// 方法规则 :修饰符 + 返回值类型 + 方法名(参数类型,参数名称)
public void sing(){
System.out.println("正在唱歌...");
}
private int sum(int a,int b){
int c = a+b;
return c;
}
// 自己的类中访问私有属性
public void showInfo(){
float height = 1.8f; // 局部变量 -必须赋值 - 外部无法访问
System.out.println("姓名:" +name+"年龄:" +age);
}
}
//类的实例化
public class App{
// 类中方法的语法格式:
// 修饰符 + 返回值类型 + 方法名(参数类型 参数名称){方法体语句:}
// 修饰符 : (public - 公开方法) (protected-类内+同包+子类) (缺省-类内部+同包) (private-内部调用)
// 返回值类型 : return 语句传递返回值,没有返回值用:void
// static 修饰称为静态方法,被调用时 ,不需要进行实例化类,直接通过类名调用方法即可
// 在静态方法中不能使用非静态变量
public static void main(String[] args){
// 再调用类中方法前,需要创建方法的类,创建一个Person类型对象,名为person1
Person person1 = new Person();
// 调用person 实例化后person1中的sum方法,传入a,b两个参数,用 int 类型的 result进行接收返回值
int result = person1.sum(a,b)
// 创建一个Person类型对象,名为person2
Person person2 = new Person();
System.out.println(person1.name);
System.out.println(person1.age); // 无法访问,私有属性
}}
5.3、不定参数的使用
public class App{
public static void main(String[] args){
// 定义一个方法用于计算多个整数的和
App.sum(1,2,3)
}
public static int sum(int ... a){
//采用循环遍历参数
int result = 0
for(int i = 0;i<a.length;i++){
System.out.println(a[i])
result = a[i]+result; // result += a[i]
}
return result;
}
}
5.4、构造方法
// 构造方法与类名完全相同,并且没有返回类型,连void都不允许有
class Person{
// 构造方法
Person(){
"构造方法体"
}
}
5.4.1、this 基本概念
- 再构造方法中 ,代表当前正在构造的对象
- 成员方法中,代表当前调用的对象
public class ThisTest{
ThisTest(){
// 当前正在构造的对象
System.out.println("构造方法: this" + this )
}
void show(){
// 自定义成员方法(谁调用show 就是谁)
System.out.println("成员方法")
}
}
5.4.2、this懒人原则
public class Person{
String name;
int age;
// 成员变量与方法参数名称一致时
Person(String name,int age){
this.name = name;
this.age = age;
}
Person getPerson(){
// 返回当前调用对象
return this;
}
}
public class Boy{
String name;
Boy(){
System.out.pringln("无参数")
}
Boy(String name){
// 调用无参方法
this();
System.out.pringln("有参数"+ name)
}
}
5.5、方法的重载
public class App{
public static void main(String[] args){
// 定义一个方法用于计算多个整数的和
// 根据传参的数据类型不同,自动识别调用哪一个函数,参数个数也可以识别
App.sum(1,2,3)
}
public static int sum(int a,int b){
return a+b;
}
public static double sum(double a,double b){
return a+b;
}
}
5.6、JAVA 继承
// 通过extends关键字可以声明一个类是从另外一个类继承而来
// 格式如下:
class "父类名"{
// 体函数
}
class "子类名" extends "父类名"{
// 子类继承父类内容 + 新写入的内容
}
//定义动物类
public class Animal{
public String name;
public int id;
public void eat(){
System.out.println(name+"正在吃饭");
}
public void sleep(){
System.out.println(name+"正在睡觉");
}
public void showInfo(){
System.out.println("我叫"+name+"我的编号是"+id);
}
}
// 定义老鼠类,继承Animal - 不支持 同时继承两个父类
// 私有的部分 private 不能被继承
public class Mouse extends Animal{}
// 定义企鹅类,继承Animal
public class Penguin extends Animal{}
// 类的继承后调用其方法依然有效
public class App{
public static void main(String[] args){
Penguin penguin = new Penguin();
penguin.id =1
penguin.name = "小企鹅";
penguin.eat();
penguin.sleep();
penguin.showInfo();
}
}
5.7、定义抽象类
- 当一个类继承抽象类后,必须重写抽象方法,否则该类也将变成抽象类
- 抽象类具有强制性和规范性,因此叫做模板设计模式
- 抽象类 : 不能具体实例化的类,并且使用abstract关键字修饰,不能创建对象
- 抽象类可以有成员变量,可以有构造方法,成员方法
- 抽象方法:不能具体实现的方法,并且使用abstract关键字修饰,没有方法体
// 定义抽象类 - 可定义抽象方法
public abstract class Animal{
public String name;
public int id;
// 定义抽象方法 - 用于强制子类重写,子类继承时必须重写
public abstract void test();
// 子类中对test方法进行了重写 、但是想调用父类中的test方法 - 通过 super 完成
super.test();
// 调用当前类的test() 方法
this.test()
}
// final 修饰的类不能被继承
public final class "类名"{
//类体
}
5.8、final 类
- final关键字修饰 类 ,该类不能被继承
- final关键字修饰 成员方法, 不能被重写,但可以被继承
- final关键字修饰 成员变量,该变量必须初始化,且不能改变
// final 类不能被任何子类所继承
public final class Animal{}
5.9、常见异常
- 算数异常
- 数组下标越界异常
- 空指针异常 - NullPointertion
5.10、封装的概念
- 为了避免一些错误的发生,需要对成员变量进行一些密封包装处理,来隐藏成员变量的细节及保证变量值得合理性,该机制 被叫做封装
/*
1、私有化成员变量 - private(私有化) - 只能在类的内部使用
2、提供公有的(public) get、set 方法,并在方法体中进行合理值的判断
3、在构造方法中也需要调用set方法 进行合理判断
*/
public class Student{
private int id;
private String name;
// 构造方法
public Student(){}
public Student(int id,String name){
setId(id);
setName(name);
}
public int getId(){
return id;
}
public int setId(int id){
if(id > 0){
this.id = id;
}else{
System.out.println("学号不合理")
}
}
public String getName(){
return name;
}
public void setName(String name){
this.name = name;
}
void show(){
System.out.println("我是" + name + ",我的学号是" + id);
System.out.println("我是" + getName() + ",我的学号是" + getId());
}
}
public class StudentTest{
public static void main(String[] args){
Student s1 = new Student();
s1.setId(1001);
s.setName("张飞");
s1.show();
Student s2 = new Student(-1001,"张飞");
s2.show;
}
}
public class StudentTest2{
public static void main(String[] args){
System.out.println("请输入学生人数");
Scanner sc = new Scanner(System.in);
int num = sc.nextInt();
// 声明长度为 num,类型为 Student类型的一维数组,其中每个元素都看作是 Student类型的变量
Student[] arr = new Student[num];
// 提示用户输入每个学生的信息(学号 姓名)并记录到一维数组中
for(int i =1;i<num;i++){
System.out.println("请输入第" + (i+1) + "个学生的信息(学号、姓名):");
arr[i] = new Student(sc.nextInt(),sc.next());
}
// 打印所有学生的类
System.out.println("该班级的所有学生信息有"):
for (int i =0;i<num;i++){
System.out.println(arr[i]); // 此处打印出来的是地址
arr[i].show()
}
}
}
5.11、static 关键字
- static 修饰成员变量,将变量由对象层级提升为类层级,被整个类共享,该成员随着加载准备就绪,与是否创建对象无关( 类名.变量名 )
- 对象中只要有一个对象改变了它的值,那么全员在使用时都使用改变后的值。一共只有一个对象
- 非静态成员方法中,既能访问非静态成员又能访问静态成员(成员 : 成员变量+成员方法,静态成员被所有对象共享)
- 在静态方法中,只能访问静态成员,不能访问非静态成员(成员 : 成员变量+成员方法,因为此时可能还没有创建对象)
public class people{
private String name;
private int age;
private static String country; // 静态变量,全员共享
public People(){}
public People(String name,int agey){
setName(name);
setAge(age);
setCountry(country);
}
public String getName(){
return name;
}
public void setName(String name){
this.name = name;
}
public void setAge(){
return age;
}
public void setAge(int age){
if(age>0 && age <15){
this.age = age;
}else{
System.out.println("年龄不合理")
}
}
}
public class StaticTest{
private int cnt = 1; // 隶属于对象层级,每个对象都有独立的一份
private static int snt = 2; // 隶属类层级,所有的对象共享一个
// 定义非静态成员方法
public void show(){
System.out.println("cnt=" + cnt); // 1
System.out.println("cnt=" + snt); // 2
}
//自定义静态成员方法,推荐使用 类名.的方式 访问
public static void test(){
System.out.println("cnt=" + cnt); // 静态成员方法只能调用静态成员变量
System.out.println("cnt=" + snt); // 静态成员方法没有this 关键字,因为是通过类名.的方式调用的
}
}
public static void main(String[] args){
staticTest st = new StaticTest();
st.show();
StaticTest.test();
}
5.12、构造块和静态代码块
- 构造块 : 在类体中直接使用{}括起来的代码块
- 每创建一个对象 都会执行一次构造块
- 静态代码块 : 用static 修饰的 代码块称为静态代码块
- 静态代码块只执行一次就完毕了,先于构造块执行
/*
执行顺序 - 静态代码块 - 构造块(对成员变量统一的初始化) - 构造方法体 - 构造块 - 构造方法体
*/
public class BlockTest{
// 构造块 - 随着对象创建而每次都会执行
{ System.out.println("构造块- 准备工作写于此处");} // 构造块
// 静态代码块 - 用 static 修饰的代构造块
static {System.out.println("静态代码块");}
// 构造方法
public BlockTest(){
System.out.println("构造方法体")
}
// 静态代码块 - 用 static 修饰的代构造块
public static void main(String[] args){
BlockTest bt = new BlockTest();
}
}
5.12.1、static 案例、
/*
编程实现Singleton类的封装
实现SingletonTest类对Singleton 类进行测试,要求 main 方法中能得到且只能得到该类的一个对象
在Singleton 中封装new 方法
*/
public class Singleton{
// 2.声明本类类型的引用指向本类类型对象 - 对象层级提升类层级
private static Singleton s1 = new Singleton();
// 1.私有化构造方法-不能再类外不能 new
private Singleton(){};
// 3.提供公有的get方法将对象返回出去
public static Singleton getInstance(){
return s1;
}
}
public class SingletonTest{
public static void main(String[] args)
//Singleton s1 = new Singleton();
//Singleton s2 = new Singleton();
Singleton s1 = Singleton.getInstance();
Singleton s2 = Singleton.getInstance();
System.out.println(s1==s2) // true
}
5.12.2、单例类
- 对外提供且只提供一个对象,这样的类叫做单例类
- 而设计单例的流程和思想叫做单例设计模式
- 任务管理器,操作多次都提供一个类
public class Singleton{
// 声明本类类型的引用指向本类类型的对象,使用private static 关键字共同修饰
private static Singleton sin = new Singleton();
// 私有化构造方法,使用private 关键字修饰
private Singleton(){}
// 提供共有的 get 方法负责将对象返回出去,使用public关键字共同修饰
public static Singleton getInstance(){
return sin;
}
}
继承特点
- 子类不能继承父类的构造方法和私有方法,但私有成员变量可以继承只是不能直接访问
- 无论使用何种方式构造子类的对象时,都会自动调用父类的无参构造方法,来初始化父类中继承的成员变量,相当于在构造方法的第一行添加代码 super() 效果。
- 子类一定是一个父类
public class Worker extends Person{}
方法重写原则
- 要求方法名相同,参数列表相同以及返回值类型相同,java5开始允许返回子类类型
- 要求方法的访问权限不能变小,可以相同或变大
- 要求方法不能排除更大的异常(异常机制)
方法重载的执行次序
/*
1.静态代码块
2.构造块
3.构造方法体
*/
/*
1.父类的静态代码块
2.子类的静态代码块
3.父类构造块
4.父类构造方法
5.子类构造块
6.子类构造方法
*/
5.13、多态的概念
- 同一种事物表现出来的多种形态
- 多态引用方法,编译阶段调用父类方法,运行阶段调用子类的方法
- 当父类类型的引用指向子类类型对象时,父类类型的引用可以调用父类中独有的方法
- 当父类类型的引用指向子类类型对象时,父类类型的引用不能直接调用子类类型的独有方法
- 对父子类都有的非静态方法,编译阶段调用父类,运行阶段调用子类的重写版本
- 对父子类都有的静态方法,编译阶段和运行阶段都调用父类
- 引用类型的转化必须发生在父子类之间
5.13.1、多态语法形式
/*
父类类型 引用变量名 = new 子类类型()
*/
Shape sr = new Rect // getlen() 子类方法
((Rect) sr).getLen(); // 使用父类强制调用子类的独有方法, 将sr 类型强制转换为 子类类型
多态案例
/*
实现Shape类封装,特征有:横纵坐标,要求提供打印所有特征的方法
编程实现 Rect 类的封装并继承自Shape类,特征:长度和宽度
实现编程 ShapeTest 类,可以根据传入特征打印特征(矩形、圆形等等)
*/
public class ShapeTest {
public static void draw(Rect r){
r.show();
}
public static void draw(Circle r){
r.show();
}
// 此处形成多态(多态顶替上面的 draw)
public static void draw(Shape r){
r.show();
}
public static void main(String[] args){
// Rect r = new Rect(1,2,3,4)
ShapeTest.draw(new Rect(1,2,3,4));
ShapeTest.draw(new Circle(1,2,3,4));
}
}
抽象方法/抽象类 abstract
- 抽象方法 : 不能具体实现的方法并且用abstract 关键字修饰,也就是没有 方法体
- 抽象类 :不能具体实例化的类(不能实例化),需要用abstract关键字修饰
- 抽象类中可以有抽象方法,也可以 没有
- 只有抽象类才能包含抽象方法
- 抽象类的实际意义在于被继承,实现多态。
- 抽象类的继承需要重写抽象类中的方法,或用抽象类继承抽象类的方式来实现
// 具体格式
/*
访问权限 abstract 返回值类型 方法名(参数列表);
*/
public abstract void cry(); // 抽象方法(宠物的叫声)
//抽象类 -不能 new对象
public abstract class AbatractTest{
// 定义成员变量、变量封装、初始化方法
// 自定义抽象方法
public abstract void show();
public static void main(String[] args){
}
}
抽象类的应用
/*
银行有定期账户和货期账户,继承自账户类,账户类中
*/
public abstract class Account {
private int money;
public Account() {
}
public Account(int money) {
this.money = money;
}
public int getMoney() {
return money;
}
public void setMoney(int money) {
if (money>0){
this.money = money;
}else{System.out.println("您输入的金额不正确");}
}
// 自定义抽象方法
public abstract double getLixi();
}
public class FixedAccount extends Account{
public FixedAccount(int i) {
super(i);
}
@Override
public double getLixi() {
return getMoney();
}
public static void main(String[] args){
Account acc = new FixedAccount(1000);
}
}
引用数据类型之间的转化
- 若强转的目标类型并不是该引用真正指向的类型数据时,则编译通过,否则运行阶段发生类型转化异常
- 为避免发生上述错误错误,应该在强转化之前进行判断 - 用此判断 if(引用变量 instanceof 数据类型)
- 判断引用变量指向的对象是为后面的数据类型
5.14、接口概念
- 接口就是一种比抽象类还抽象的类,体现在所有方法都是抽象方法
- 定义类的关键字为 class, 而定义 接口的关键字是 interface
- 接口支持多继承
public interface InterfaceTest{
/* pulic static final */ int CNT = 1 //里面只能有常量
public abstract void show(); //里面只能有抽象方法,不能有方法体(新特性除外)
}
5.14.1、接口的多实现
// 金属接口
public interface Metal{
// 自定义抽象方法描述发光行为
pulic abstract void shine();
}
// Money 接口
public interface Money{
// 自定义购物行为
pulic abstract void buy();
}
// 黄金类实现前面个两个接口功能
// 使用 implements 关键字表达实现关系,支持多实现
public class Gold implements Metal,Money{
// 重写接口的方法
@Override
public void shine(){
System.out.println("发光")
}
@Override
public void buy(){
System.out.println("买了好吃的")
}
public static void main(String[] args){
// 声明接口类型引用指向实现类的对象,形成多态
Metal mt = new Gold();
mt.shine();
Money mn = new Gold();
mn.buy();
}
}
public interface Runner{
// 自定义奔跑的行为
pulic abstract void buy();
}
// 接口只能继承接口不能继承类
public interface Hunter extends Runner{
// 自定义成员方法描述捕猎行为
public abstract void hunt();
}
// 接口的实现 关键字 implements
public class Man implements Hunter{
@Override
public void hunt(){
System.out.println("追赶小白兔")}
@Override
public void run(){System.out.println("正在奔跑") }
/*
java8之后只增接口中可增加默认方法
默认的重写方法(default关键字)
增加静态方法,隶属 接口层级,通过接口名直接调用
*/
public default show1(){
}
// 增加静态方法,隶属于类层级,也就是接口层级
public static void test(){
System.out.println("静态方法,可直接通过接口名.的方式调用,省略对象的创建")
}
public static void main(String[] args){
// 声明接口类型的引用指向实现类的对象,形成多态
Runner runner = new Man();
runner.run();
Hunter hunter = new Man();
hunter.hunt();
// 使用接口名称的方式调用接口的静态方法
hunter.test();
}
}
5.14.2、接口和抽象类的区别
- 定义抽象类的关键字是 abstract class , 而定义接口的关键字是 interface
- 继承抽象类关键字是 extends, 而实现接口的关键字是 implements.
- 继承只能单继承,而接口可以多实现
- 抽象类中可以有构造方法,变量,而接口中不可以有构造方法,只能有常量。
- 抽象类中可以有成员方法,而接口中只可以有抽象方法
- 抽象类中增加方法时子类可以不用重写,而接口中增加方法时实现类需要重写
- 接口允许出现非 抽象方法和静态方法,但非抽象类需要使用 default 关键字修饰
- java9 开始新增加特性,接口允许出现私有化方法
5.15、内部类的概念
- 当类定义出现另外一个类的类体中时,那么这个类叫做内部类(Inner),而这个内部类所有的类叫做外部类(Outer)
- 类中的内容 : 成员变量、成员方法、构造方法、静态成员、构造块和静态代码块、内部类。
- 内部类的价值 : 只为一个类服务,外部不可见,可以方便的访问外部的私有成员而不需要提供共有的get 和set 方法
5.15.1、内部类的分类
- 普通内部类 - 直接将一个类的定义放在另外一个类的类体中
- 静态内部类 - 使用static 关键字修饰内部类,隶属于类层级
- 局部内部类 - 直接将一个类的定义在方法体的内部时
- 匿名内部类 - 指没有名字的内部类
5.15.1.1、普通(成员)内部类
/**
普通内部类和普通类一样可以定义成员变量,成员方法,构造方法等
普通内部类和普通类一样可以使用 final或者 abstract关键字修饰
普通内部类还可以使用private 或 pritected关键字进行修饰
普通内部类需要使用内部类对象来创建
如果内部类访问外部类中与本类内部同名的成员变量或方法时,需要使用this关键字
*/
pubic class NormalOuter{
private int cnt = 1;
// 定义内部类
public class NormalInner{
private int ia =2;
public NonmalInner(){
System.out.println("普通内部类的构造方法")
}
public void show(){
// 调用外部类的成员变量
System.out.println("外部类中变量cnt的数值为" + cnt)
System.out.println("ia" + ia)
}
public void show2(int cnt){
System.out.println("形参变量 cnt=" + cnt) // 局部优先原则
System.out.println("内部类中 cnt=" + this.cnt) // 打印内部类中的 cnt变量
System.out.println("外部类中 cnt=" + NormalOuter.this.cnt) // 局部类中的 cnt变量
}
}
}
// 内部类方法的调用
public class NormalOutTest{
public static void main(String[] args){
// 先创建外部类
NormalOuter no = new NormalOuter();
// 内部类指向内部对象
NormalOuter.NormalInner ni = no.new NormalInner();
ni.show();
}
}
5.15.1.2、静态内部类
- 加入static关键字后,变成类层级,调用 用 类点 的形式调用
- 静态方法只能访问静态成员变量
- 静态内部类不能访问外部类的非静态成员
- 静态内部类可以直接创建对象
public class StaticOuter{
private int cnt = 1; // 隶属于对象层级
private static int snt =2; // 隶属于类层级
// 定义静态内部类
public static class StaticInner{
private int ia = 3;
private static int snt = 4;
public StaticInner(){
System.out.println("静态内部类的构造方法!");
}
public void show(){
System.out.println("ia = " + ia);
System.out.println("外部类中的snt = " + snt);
}
// 使用不同位置的参数
public void show2(int snt){ //就近原则
System.out.println("snt = " + snt); // 调用外部传递过来的
System.out.println("内部类成员 snt = " + StaticTnner.snt); // 内部类的参数
System.out.println("外部类成员 snt = " + StaticOuter.snt); // 外部类中的参数
}
}
}
// 内部类方法的调用
public class StaticOuterTest{
public static void main(String[] args){
// 无需创建外部类直接 创建内部类即可
StaticOuter.staticInner si = new StaticOuter.staticInner();
si.show();
}
}
5.15.1.3、局部内部类
- 将类写在方法体的内部的类
访问修饰符 class 外部类的类名{
访问修饰符 返回值类型 成员方法名(形参列表){
class 内部类的类名{
内部体的类体;
}
}
}
public class AreaOuter{
private int cnt =1;
public void show(){
// 定义局部内部类,只在当前的方法体的内部可用
class AreaInner{
private int ia = 1;
public AreaInner(){
System.out.println("局部内部类的构造方法");
}
public void test(){
System.out.println("ia =" + ia);
System.out.println("cnt =" + cnt);
}
}
// 声明局部内部类的引用指向局部内部类的对象
AreaInner ai = new AreaInner();
ai.test();
}
}
// 运行调用
public class AreaOuter{
public static viod main(String[] args){
// 声明 外部类类型的引用指向外部类的对象
AreaOuter ao = new AreaOuter();
// 通过show 方法调用实现局部内容类的定义和使用
ao.show();
}
}
5.15.1.4、局部内部类的使用方式
- 局部内部类只能在该方法的内部使用
- 局部内部类可以 在方法体内部直接创建对象
- 局部内部类不能使用 访问控制符和 static 关键字修饰符
- 局部内部类 可以使用外部方法的局部变量,但是必须是final的。由局部内部类 和局部变量的声明周期不同所致
5.15.1.5、回调模式
- 回调模式 — 如果一个方法的参数是接口类型,则在调用该方法时,需要创建并传递一个实现此接口类型的对象;而该方法在运行时会调用到参数对象中 所有实现的方法(接口中定义的)
// 定义一个接口
public interface AnonymousInterface{
// 自定义接口的抽象方法
public abstract void show();
}
// 实现接口的类
public class AnonymousInterfaceImpl implements AnonymousInterface{
@Override
public void show(){
System.out.println("这是接口的实现类")
}
}
public class AnonymousInterfaceTest{
// 假设已有下面方法
public static void test(AnonymousInterface ai){
ai.show();
}
public static void main(String[] args){
// AnonymousInterface ai = new AnonymousInterfaceImpl()
AnonymousInterfaceTest(new AnonymousInterfaceImpl());
}
}
5.15.1.6、匿名内部类
- 当接口/类类型的引用作为方法的形参时,实参的传递方式有两种
- 自定义类实现接口/继承类并重写方法,然后创建该类对象作为实参传递
- 使用上述匿名内部类的语法格式得到接口/类类型的索引即可
匿名内部类的语法格式
接口/父类类型 引用变量名 = new 接口/父类类型(){ 方法的重写 }
public class AnonymousInterfaceTest{
public static void test(AnonymousInterface ai){
ai.show();
}
public static void main(String[] args){
AnonymousInterfaceTest.test(new AnonymousInterfaceImpl());
AnonymousInterface ait = new AnonymousInterface(){
@Override
public void show(){
System.out.println("这里是接口的实现类");
}
}
// 从java8 开始提出新特性 lamda 表达式可以简化上述代码,格式为:(参数列表)->{方法体}
AnonymousInterface ait =() -> System.out.println("lamda 表达式原来是如此简单")
AnonymousInterfaceTest.test(ait2);
}
}
5.16、枚举定义
- 使用 public static final 表示的常量描述 较为繁琐,使用 enum关键字来定义枚举类型取代常量,美剧类型是从java5 开始增加的一种引用数据类型
- 枚举值就是当前类的类型,也就是指向本类的对象,默认使用public static final 关键字共同修饰,因此采用枚举类型 . 的方式调用。
- 枚举类可以自定义构造方法,但是构造方法的修饰符必须是private, 默认也是私有的
/*
编程实现所有方向的枚举,所有的方向,向上,向下,向左,向右 枚举类型要求所有枚举必须放在枚举类型的最前面
*/
public enum DirectionEnum{
// 声明本类类型的引用指向类类型的对象,需要放在最前面
UP(desc:"向下"),DOWN(desc:"向下"),LEFT(desc:"向左"),RIGHT(desc:"向右");
private final String desc; // 用于描述方向字符串的成员变量
private DirectionEnum(String desc){
this.desc = desc;
}
// 通过公有的get方法可以在本类的外部访问该类成员变量的数值
public String getDesc(){
return desc;
}
}
// 枚举类型的调用
public class DirectionTest{
public static void mian(String[] args){
DirectionEnum de = DirectionEnum.DOWN;
System.out.println("方向为:" + de.getDesc());
}
5.16.1、枚举类型
public class Direction{
// final 修饰成员变量,必须初始化,且不能改变
private final String desc; // 用于描述方向字符串的成员变量
// 通过构造方法实现成员变量的初始化,更加灵活
public Direction(String desc){
this.desc = desc;
}
// 1.为了不让外部不能随意的调取构造方法,所以进行私有化
private Direction(String desc){
this.desc = desc;
}
// 2.声明本类类型的引用指向本类类型的对象
public static final Direct UP = new Direction(desc:"向上");
public static final Direct DOWN = new Direction(desc:"向下");
public static final Direct LEFT = new Direction(desc:"向左");
public static final Direct RIGHT = new Direction(desc:"向右");
// 通过公有的get 方法可可以在本类的外部访问该类成员变量的数值
public String getDesc(){
return desc;
}
}
public class DirectionTest{
public static void mian(String[] args){
// 声明Direction类型的引用指向该类型的对象并打印特征
Direction d1 = new Direction(desc:"向上");
System.out.println("获取到字符串是:" + d1.getDesc()); // 向上
Direction d2 = new Direction(desc:"向下");
System.out.println("获取到字符串是:" + d2.getDesc()); // 向下
Direction d3 = new Direction(desc:"向左");
System.out.println("获取到字符串是:" + d3.getDesc()); // 向左
Direction d4 = new Direction(desc:"向右");
System.out.println("获取到字符串是:" + d4.getDesc()); // 向右
Diretion d5 = Direction.UP;
System.out.println("获取到的方向是" + d1.getDesc()); // 向上
}
}
5.16.2、自定义类和枚举类型使用的区别
public class DirectionUseTest{
// 自定义静态方法实现根据参数指定的字符串内容,打印具体的方向信息
public static void test(String str){
switch(str){
case "向上":
System.out.println("向上的部分");break;
case "向下":
System.out.println("向下的部分");break;
case "向左":
System.out.println("向左的部分");break;
case "向右":
System.out.println("向右的部分");break;
default:
System.out.println("没有这样的方法")
}
}
public static void test2(DirectionEnum de){
switch(de){
case UP:
System.out.println("向上的部分");break;
case DOWN:
System.out.println("向下的部分");break;
case LEFT:
System.out.println("向左的部分");break;
case RIGHT:
System.out.println("向右的部分");break;
default:
System.out.println("没有这样的方法")
}
}
public static void main(String[] args){
DirectionUserTest.test1(Direction.UP.getDesc());
DirectionUseTest.test2(DirectionEnum.DOWN);
}
}
5.16.3、Enum类的概念和方法
- 所有的枚举类都默认继承自 java.lang.Enum类,常用方法如下
方法 | 方法解析 |
---|---|
static T[] values() | 返回当前枚举类中的所有对象 |
String toString() | 返回当前枚举对象的名称 |
int ordinal() | 获取枚举对象在枚举类中的索引位置 |
static T valueOf(String str) | 将参数指定的 字符串名转为当前枚举类的对象 |
int compare To(E o) | 比较两个枚举对象在定义时的 顺序 |
注: T 是type 的首字母 |
public class DirectionEnumTest{
public static void main(String[] args){
//1.获取 DirectionEnum 类型中所有的枚举对象
DirectionEnum[] arr = DirectionEnum.values();
//2.打印每个枚举对象在枚举类型中的名称和索引位置
for(int i =2;i<arr.length;i++){
System.out.println("获取的枚举对象名称是" + arr[i].toString());
System.out.println("获取的枚举对象对应的索引位置是" + arr[i].ordinal());
}
// 3.根据参数指定的字符串得到枚举类型的对象,也就是将字符串转换成对象
DirectionEnum de = DirectionEnum.valueOf("DOWN");
System.out.println("转换出来的枚举对象名称是" + de.toString());
// 可以不写 de.toString 当打印引用变量是,回自动调用 toString 方法
System.out.println("转换出来的枚举对象名称是" + de);
for (int i =0;i<arr.length;i++){
// 当调用对象在参数对象之后时,获取到的比较结果为 正数
// 当调用对象在参数对象相同位置时,获取到的比较结果为 0
// 当调用对象在参数对象之前位置时,获取到的比较结果为 负数
System.out.println("调用对象与数组中对象比较先后顺序结果是:" + de.compareTo(arr[i]))
}
}
}
5.16.4、枚举类型实现接口
- 枚举类实现接口后需要重写抽象方法,而重写方法的方式有两种:重写一个,或每个对象都重写
- 枚举每个对象都重写 是匿名
// 定义一个接口
public interface DirectionInterface{
// 自定义抽象方法
public absrtact void show();
}
public enum DirectionEnum implements DirectionInterface{
// 声明本类类型的引用指向类类型的对象
UP(desc:"向上"),DOWN(desc:"向下"),LEFT(desc:"向左"),RIGHT(desc:"向右")
// 每个对象都重写
UP(desc:"向上"){
@Override
public void show(){}
}
private final String desc; // 用于描述方向字符串的成员变量
private DirectionEnum(String desc){
this.desc = desc;
}
public String getDesc(){return desc;}
// 重写接口中的方法(整个枚举类型重写一次,所有对象调用同一个)
@Override
public void show(){
System.out.println("此处重写了接口中的show方法");
}
}
5.17、注解的概念
- 注解又叫标注,是从java5开始增加的一种引用引用数据类型
- 注解 本质上就是代码中特殊标记,通过这些标记可以在编译阶段、类加载阶段、以及运行时执行指定的处理
- 自定义注解自动继承 java.lang.annotation.Annotation接口
- 通过@注解名称的方式可以 修饰包、类、成员方法、成员变量、构造方法、参数、局部变量的声明
- 注解体中只有成员变量没有成员方法,而注解的成员变量以"无形参的方法"形式来声明,其方法名定义了该成员变量的名字,其返回值定义了该成员变量的类型
- 注解的成员类型
注解的语法格式 - 实际上注解是一种特殊的接口
访问修饰符 @interface 注解名称{
注解成员;
}
// 注解(若一个注解中没有任何成员,则这样的注解叫做标记注解/标识注解)
public @interface MyAnnotation{
}
// 注解的使用,表示将标签 MyAnnotation贴到person上
@MyAnnotation
public class Person{}
// 注解中定义成员变量
public @interface MyAnnotation{
public String value(); // 声明一个 String类型的成员变量,名字为 value
public String value2();
// 若注解中成员变量使用时不给值,则需要使用default关键字,给一个默认值
public String value() default "123";
}
// 注解的使用,注解函数中有成员变量,赋值
@MyAnnotation(value="hello",value2="world")
public class Person{}
5.18、元注解
- 可以注解到注解上的注解,或者说元注解是一种基于注解,但是它能够应用到其他注解上面
- @Retention - 描述注解的范围,及有效周期
- @ Documented - 注解是否在文档描述中体现
- @ Target - 注解到底 可以修饰哪些内容
- @Inherited - 注解是否可以被继承到所标记类的子类中
- @ Repeatable - 是否可以重复
/*
元注解
RetentionPolicy.SOURCE 注解只有在源码阶段保留,在编译器进行编译时它被丢弃
RetentionPolicy.CLASS 注解只被保留到编译进行的时候,它并不会被加载到JVM中,默认方式。
*/
6、JAVA常用核心类库
- java.lang - 核心包,所有 内容由虚拟机自动导入。如 : System类、String类。
- java.util包 - 工具包、提供大量的工具类及集合类。 如 Scanner类、Random类、List 集合。
- java.io包 - 输入输出包,包含大量的读写文件相关的类。 如:FileInputStream类、FileOutputSteam类。
- java.net包 - 网络包 ,提供大量网络编程相关的类。如 : ServerSocket类、Socket类。
- java.sql包 - 数据包,提供大量操作数据库的类和接口。如:DriverManager类、Connection接口
6.1、java.lang 包
// equals 方法的重写
@Override
public boolean equals(Object obj){
// 调用对象和参数对象指向通一对象(地址),则内容一定相同
if(this == object) return true;
// 调用对象不为空,参数对象为空,则内容一定不相同
if(null == obj) return false;
// 判断 参数obj 指向对象是否为 Student类型对象,若是则条件成立,否则条件不成立
if(obj instanceof Student){
Student ts = (Student) obj;
return this.getId() == ts.getId();
}
return false;
}
// 重写 hashCode 方法(重写 equals 就需要重写 hashcode)
@Override
public int hashCode(){
return getId();
}
// String toString 获取调用对象的字符串形式 包名.类名@哈希码值得十六进制
6.2、java.date 包
public class dataTest {
public static void main(String[] args) {
// 当前系统时间
Date d1 = new Date();
System.out.println("d1 = " + d1);
// 获取对象距离1970年1月1日0点0分0秒的毫秒数
Date d2 = new Date(1000);
System.out.println("d2 = " + d2);
// 3.获取调用对象距离1970年1月1日0时0分0秒的毫秒数
long mesc = d2.getTime();
System.out.println("获取d2到1970的毫秒数为" + mesc);
//4、设置调用对象的时间点
d2.setTime(2000);
System.out.println("修改后的时间为" + d2);
}
}
import java.text.SimpleDateFormat;
import java.util.Date;
public class SimpleDateFormatTest {
public static void main(String[] args) throws Exception {
//1.获取系统当前时间
Date d1 = new Date();
System.out.println("d1 =" + d1 );
//2.构造SimpleDateFormat类型的对象并指定格式
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
//3.实现日期格式向文本类型的转换并打印
//alt + Enter 可以实现返回值的生成
String format = sdf.format(d1);
System.out.println("转换后的日期为" + format);
//4.实现文本类型到日期类型的转化打印
Date parse =sdf.parse(format);
System.out.println("转化日期格式的结果为" + parse);
}
}
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
public class CalendarTest {
public static void main(String[] args) {
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
//1.使用指定的时间来构造对象
Calendar instance = Calendar.getInstance();
//2.设置日期对象的格式及打印
instance.set(2008,8,8,20,8,8);
//3.转化为DATE类型的对象
Date d2 = instance.getTime();
String format1 = sdf.format(d2);
System.out.println("获取到的时间为" + format1);
// 向指定的字段是指或增加数值
instance.set(Calendar.YEAR,2018);
Date d3 = instance.getTime();
System.out.println("设置后的年份结果为" + sdf.format(d3));
instance.add(Calendar.MONTH,2);
Date d4 = instance.getTime();
System.out.println("设置后的年份结果为" + sdf.format(d4));
}
}
6.3、集合
记录多类型不同得对象数据,称之为集合
java.util.Collection 集合 (单个元素)
java.util.Map 集合 (字典格式)
方法声明 | 功能介绍 |
---|---|
boolean add() | 向集合中添加元素 |
boolean addAll(Collection) | 将参数集合中得所有元素添加到调用集合中 |
boolean contains(Object o) | 判断是包含 |
boolean containsAll(Collection) | 判断调用集合是否包含参数集合 |
boolean retainAll(Collection)) | 保留两个集合得公共部分,交集 |
boolean remove(Object o) | 从集合中删除 |
boolean removeAll((Collection) | 删除集合中指定得所有对象 |
void clear() | 清空集合 |
int size() | 返回集合对象个数 |
boolean isEmpty() | 判断集合是否为空 |
boolean equals(object o) | 判断是否相等 |
int hashCode() | 获取当前集合得哈希码值 |
object[] toArray() | 将集合转换为数组 |
Iteratoriterator() | 获取当前集合得迭代器 |
6.3.1 Iterator接口
- java.util.lterator接口用于描述 迭代器对象,可以遍历Colletction集合的所有元素
- java.util Collection 接口继承 lterator接口,因此所有实现Collection接口类都可以使用迭代器对象
方法声明 | 功能介绍 |
---|---|
boolean hasNext() | 判断集合中是否有可以迭代/访问的元素 |
E next() | 用于取出一个元素并指向下一个元素 |
void remove() | 用于删除访问到的最后一个元素 |
import sun.plugin.javascript.navig.Array;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.Objects;
public class CollectionTest {
public static void main(String[] args){
// Collection c1 = new Collection(); 此语句无效,因为 Collection 为接口,不能创建对象
Collection c1 = new ArrayList();
boolean b1 = c1.add(new String("one"));
System.out.println("集合中的元素为" + c1);
b1 = c1.add(Integer.valueOf(2));
System.out.println("b1 =" + b1);
System.out.println("集合中的元素为" + c1);
b1 = c1.add(new Person("张飞",30));
System.out.println("集合中的元素为" + c1); // 集合中的元素为[one, 2, Person{name='张飞', age=30}]
Collection c2 = new ArrayList();
c2.add("three");
c2.add(4);
System.out.println("集合中的元素为" + c2); //集合中的元素为[three, 4]
c1.addAll(c2);
System.out.println("集合中的元素为" + c1); // 集合中的元素为[one, 2, Person{name='张飞', age=30}, three, 4]
// 判断是否包含单个元素
b1 = c1.contains("one");
System.out.println("集合中的元素为" + b1);
b1 = c1.contains(8);
System.out.println("集合中的元素为" + b1);
// contains 通过equal 来判断是否包含,此处new中内有重写 equal所以判断为地址,需要重写equals
b1 = c1.contains(new Person("张飞",30));
System.out.println("集合中的元素为" + b1);
// 判断是否包含参数指定集合的所有
Collection c3 = new ArrayList();
c3.add(4);
b1 = c1.containsAll(c3); // c1 中是否包含c3的全部元素 true
// 计算两个集合的交集,如果当前集合改变则返回true,如果没改变则返回flase
b1 = c2.retainAll(c2);
// 计算 c2、c3的交集并保留在集合 c2中,取代c2中原有的数值
b1 = c2.retainAll(c3);
System.out.println(b1);
System.out.println(c2); // 此时c2已经发生改变,变成了 交集
// remove 方法原理equals
// 从集合中删除单个元素
b1 = c1.remove("one");
b1 = c1.remove(new Person("张飞",30));
System.out.println(b1);
System.out.println(c1);
// 从集合c1 中删除 集合 c3中的全部元素,c1 方法改变
b1 = c1.removeAll(c3);
// 清空集合
c3.clear();
System.out.println(c3);
// 判断集合元素个数
System.out.println(c1);
System.out.println(c1.size());
c1.isEmpty(); // 判断是否为空
// 集合转化成数组
Object[] objects = c2.toArray();
System.out.println("数组中元素有" + Arrays.toString(objects));
// 数组类型向集合类型的转化
Collection objects1 = Arrays.asList(objects);
// 获取当前集合的迭代器
Iterator iterator1 = c1.iterator();
System.out.println(iterator1.hasNext());
System.out.println(iterator1.next());
//使用迭代器来模拟toString方法的打印效果
// 由于上个循环已经使得迭代器走到了最后,因此需要重置迭代器
iterator1 = c1.iterator();
StringBuilder sb1 = new StringBuilder();
sb1.append("[");
while (iterator1.hasNext()){
Object obj = iterator1.next();
// 当获取元素是最后一个元素时,则拼接元素加括号
if(!iterator1.hasNext()){
sb1.append(obj).append("]");
}else{
sb1.append(obj).append(",").append(" ");
}
}
System.out.println("c1=" + sb1); // c1=[2, Person{name='张飞', age=30}, three]
// 不断得获取集合中得元素并判断,当元素值"ONE",时,则删除该元素
iterator1 = c1.iterator();
while (iterator1.hasNext()){
Object obj = iterator1.next();
if ("one".equals(obj)){
iterator1.remove();
}
}
// 使用 for eachxun循环遍历数和集合
// for(元素类型 变量名 : 数组/集合名称) {循环体}
for (Object obj : c1){
System.out.println("取出来得元素时" + obj);
}
}
}
7、异常处理机制
- java.lang.Throwable 类是错误(Error)和异常(Exception)的超类
- Exception 主要包括 运行时异常(RuntimeException)和检测性异常(IOException)
RuntimeException
- ArithmeticException 类 - 算数异常
- ArrayIndexOutOfBoundException类 - 数组下标越界异常
- NullPointerException -空指针异常
- ClassCastException - 类型转换异常
- NumberFormatException - 数字格式异常
import java.io.IOError;
import java.io.IOException;
public class ExceptionPreventTest {
public static void main(String[] args) {
// 非检测性异常,运行时异常
//System.out.println(5/0);
//检测性异常
//Thread.sleep(1000);
System.out.println("程序结束");
// 空指针异常
String str = null;
if(null!= str){
System.out.println(str.length());
};
// 类型转化异常
Exception ex = new Exception();
if(ex instanceof IOException){
IOException ie = (IOException)ex;
};
7.1、异常捕获
ctrl + alt + t
/*
try{
编辑可能发生异常代码
} catch(异常类型 引用变量名){
编写针对该异常处理代码
}
finally{
编写无论发生什么都会执行的代码
}
*/
public class ExceptionCatchTest {
public static void main(String[] args) {
//创建一个fileinputstream 类型的对象,与demo/a.text关联
FileInputStream fis = null;
try {
fis = new FileInputStream(".demo/a.text");
} catch (FileNotFoundException e) {
e.printStackTrace();
}
// 关闭文件
try {
fis.close();
} catch (IOException e) {
e.printStackTrace();
} finally {
System.out.println("最后一句");
}
}
}
7.2、异常抛出
import java.io.IOException;
public class ExceptionMethod {
public void show() throws IOException{}
}
import java.io.FileNotFoundException;
import java.io.IOException;
public class SubExcetionMethod extends ExceptionMethod{
@Override
// 子类重写的方法可以抛出和父类一样的异常
// 子类重写的方法可以抛出更小的异常
// 子类可以不抛出异常
// 不可以抛出平级不一样的异常
// 不可以抛出更大的异常
public void show() throws FileNotFoundException {}
}
7.3、自定义异常
// 定义年龄异常类
public class AgeException extends Exception{
static final long serialVersionUID = 7837261718L; // 序列化版本号
public AgeException() {
}
public AgeException(String message) {
super(message);
}
}
// 定义person 类
public class Person {
private String name;
private int age;
public Person() {
}
public Person(String name, int age) throws AgeException {
setAge(age);
setName(name);
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
// 测试类
public class PersonTest {
public static void main(String[] args) {
Person p1 = null;
try {
p1 = new Person("zhangfei",-30);
} catch (AgeException e) {
e.printStackTrace();
}
System.out.println("p1="+p1);
}
}
8、JAVA File类
- 基本概念 :java.io.File 类主要用于描述文件或目录路径的抽象表示信息,如大小
import java.io.File;
import java.io.FileFilter;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Locale;
public class FileTest {
// 实现成员方法实现 指定目录及子目录中所有内容的打印
public static void show(File file){
// 获取目录 f3 下的所有内容并记录到一维数组中
File[] filesArray = file.listFiles(); // 获取该目录下的所有内容
// 遍历数组
for(File tf:filesArray){
// 判断是否为文件
if(tf.isFile()){
System.out.println(tf.getName());
}
// 判断是否为文件夹
if(tf.isDirectory()){
System.out.println("[" + tf.getName() + "]");
show(tf);
}
}
}
public static void main(String[] args) throws IOException {
// 1.构造 File类型的对象 与 d:a.txt 文件关联
File f1= new File("d:a.txt");
// 2.若文件存在则获取文件相关信息并打印 后删除文件
if(f1.exists()){
System.out.println("文件的名称是" + f1.getName());
System.out.println("文件的大小为" + f1.length());
// System.out.println("文件最后一次的修改时间" + f1.lastModified()); 返回时间戳
Date d1 = new Date(f1.lastModified()); // 转化为时间
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm;ss"); // 修改时间格式
System.out.println("文件最后一次的修改时间" + sdf.format(d1));
System.out.println("文件的绝对路径信息是" + f1.getAbsolutePath());
System.out.println(f1.delete()? "文件删除成功": "文件删除失败");
} else{
// 3. 若文件不存在则创建新的文件夹
// 此处异常,抛出
System.out.println(f1.createNewFile()? "文件创建成功": "文件创建失败");
}
System.out.println("---------------------");
//4.实现目录的删除与创建
File f2 = new File("d:/目录1");
if(f2.exists()){
System.out.println("目录的文件名是" + f2.getName());
System.out.println(f2.delete()? "目录删除成功": "目录删除失败"); // 删除最后一级目录
} else {
System.out.println(f2.mkdir()? "目录创建成功": "目录创建失败"); // 创建单级目录
System.out.println(f2.mkdirs()? "目录创建成功": "目录创建失败"); // 创建多级目录
}
System.out.println("---------------------");
// 5.打印目录下的所有内容
File f3 = new File("d:/目录1");
// 获取目录 f3 下的所有内容并记录到一维数组中
File[] filesArray = f3.listFiles(); // 获取该目录下的所有内容
// 遍历数组
for(File tf:filesArray){
// 判断是否为文件
if(tf.isFile()){
System.out.println(tf.getName());
}
// 判断是否为文件
if(tf.isDirectory()){
System.out.println("[" + tf.getName() + "]");
}
}
// 6.实现目录中素有内容获取的同时进行过滤
// 匿名内部类的语法格式 :接口/父类类型 引用变量名 = new 接口/父类类型(){方法重写};
FileFilter fileFilter = new FileFilter() {
@Override
public boolean accept(File pathname) {
// 若文件名以 avi 结尾 则返回
return pathname.getName().endsWith(".avi");
}
};
File[] filesArray2 = f3.listFiles(fileFilter);
for(File tf:filesArray2){
System.out.println(tf);
}
// 7.使用递归思想 获取目录并打印文件名
show(new File("d:/目录1"));
}
}
9、JAVA IO流
基本概念
- IO = Input + Output, 即为输入和输出
- 分为字节流 和 字符流
- 字节流可以书写任意形式的数据
- 字符流只能以字符(2字节)为单位进行书写,只能读写 文本文件
9.1、FileWrite类
- 将文本内容写入到文本文件
import java.io.FileWriter;
import java.io.IOException;
public class FileWriterTest {
public static void main(String[] args) {
FileWriter fw = null;
try {
// 1.构造 FileWrite 类型的对象 与 文件关联
// 若文件不存在,则自动进行创建
// 若文件存在 则清空原文件内容
// fw = new FileWriter("d:/a.txt", append:true); 追加写入方式
fw = new FileWriter("d:/a.txt");
// 2.通过流 对象写入数据内容
fw.write("a");
// 写入字符数组中的内容
char[] cArr = new char[]{'h','e','l','l','o'};
fw.write(cArr,1,3); // 写入部分
fw.write(cArr); // 写入全部
//刷新流
fw.flush();
System.out.println("数据写入成功");
} catch (IOException e) {
e.printStackTrace();
} finally {
// 3. 关闭流对象并释放有关资源
if(null!=fw){
try {
fw.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
9.2、FileReader类
- 文本文件的读取
import java.io.FileReader;
import java.io.IOException;
public class FileReaderTest {
public static void main(String[] args) {
FileReader fr = null;
try {
// 1.构造 FileReader类型的对象 与文件关联
fr = new FileReader("D:/目录1");
/*
// 2.读取文件内容并打印,返回内容为字符的 ARSC码值需要转化
int res = fr.read();
System.out.println("读取到的单个字符是" + (char)res);
*/
/*
// 读取全部内容
int res =0;
while ((res = fr.read()) != -1){
System.out.println("读取到的单个字符是" + (char)res);
}
*/
/*
// 读取数组
char[] cArr = new char[5];
int res = fr.read(cArr,1,3);
System.out.println("实际读取到的字符个数是:" + res );
for(char cv:cArr){
System.out.println("读取到的字符是:" + (char)cv);
}*/
} catch (IOException e) {
e.printStackTrace();
} finally {
// 3.关闭流对象并释放有关资源
if (null != fw){
try {
fr.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
9.3、FileOutputStream类/FileInputStream类
- 用于图像数据之间的原始字节流写入流中
- 图片文件的拷贝
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
public class FileOutputStreamTest {
public static void main(String[] args) {
FileInputStream fis = null;
FileOutputStream fos = null;
try {
// 1.创建 FileInputStream 类型的对象与图片关联
fis = new FileInputStream("d:/图片1");
// 2.创建 FileOutputStream 类型的对象与图片关联
fos = new FileOutputStream("d:/图片2");
// 3.不断读取,并写入
System.out.println("正在拷贝中");
/*
拷贝方式 1 :
int res = 0;
while ((res = fis.read()) != -1){
fos.write(res);
}
*/
/*
//拷贝方式 2 :
int len = fis.available(); // 获取拷贝文件的大小
System.out.println("拷贝文件的大小是:" + len);
byte[] bArr = new byte[len];
int res = fis.read(bArr);
System.out.println("实际文件的大小为:" + res);
fos.write(bArr);
*/
// 拷贝方式 3 :
byte[] bArr = new byte[1024];
int res = 0;
while ((res = fis.read(bArr)) != -1){
fos.write(bArr,0,res);
}
System.out.println("拷贝成功");
} catch (IOException e) {
e.printStackTrace();
} finally {
if(null != fis){
try {
fos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if(null != fos){
try {
fis.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
9.4、BufferedInputStream / BufferedOutputStream
- 描述缓冲输入、输出流,不用为每个字节调用底层系统
import java.io.*;
public class BufferedByteCopyTest {
public static void main(String[] args) {
BufferedInputStream bis = null;
BufferedOutputStream bos = null;
try {
//1.创建 BufferedInputStream类 与文件关联,默认 4MB
bis = new BufferedInputStream(new FileInputStream("d:/视频1"));
//2.创建 BufferedOutputStream类 与文件关联 默认 4MB
bos = new BufferedOutputStream(new FileOutputStream("d:/视频2"));
// 3.不断的读取和写入
// 自定义缓冲区
System.out.println("正在复制中");
byte[] bArr = new byte[1024];
int res = 0;
while ((res = fis.read(bArr)) != -1){
fos.write(bArr,0,res);
}
} catch (FileNotFoundException e) {
e.printStackTrace();
} finally {
// 4.关闭流对象
if(null != bos){
try {
bos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if(null != bis){
try {
bis.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
9.5、BufferedReader / BufferedWriter
-
写入单个字符,字符数组,字符串到输出流中
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
import java.io.BufferedReader;
import java.io.BufferedWriter;
public class BufferedTest {
public static void main(String[] args) {
BufferedReader br = null;
BufferedWriter bw = null;
try {
//1.创建 BufferedReader 类型的对象 与 文件关联
br = new BufferedReader(new FileReader("d:/a.txt"));
// 2.创建 BufferedWriter 类型的对象 与 文件关联
bw = new BufferedWriter(new FileWriter("d:/b.txt"));
// 3.读取和写入
String str = null;
while ((str = br.readLine()) != null){
bw.write(str);
}
} catch (IOException e) {
e.printStackTrace();
} finally {
// 4.关闭
if(null != bw){
try {
bw.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if(null != br){
try {
br.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
9.6、PrintStream类/ PrintWrite类
- 打印各种数据内容
- 将对象的格式化形式打印到文本输出流
/*
不断的提示用户输入发送内容,若发送的内容 "bye" 则聊天结束,否则用户输入内容写入到文件中
要求使用BufferedReader 类来读取键盘的输入,System.in代表键盘输入
要求 使用PrintSteam 类负责讲数据写入文件
*/
import java.io.*;
import java.text.SimpleDateFormat;
import java.util.Date;
public class PrintWriteTest {
public static void main(String[] args) {
BufferedReader br = null;
PrintStream ps = null;
try {
// 由手册可知 : 构造方法需要是Reader类型的引用,但Reader类是一个抽象类,实参只能传递子类的对象,字符流
// 由手册可知 : System.in 代表键盘输入,而且是 InputStream类型的 字节流
br = new BufferedReader(new InputStreamReader(System.in));
ps = new PrintStream(new FileOutputStream("d:/a.txt"));
// 声明一个boolean 类型的变量作为发送方的代表
boolean flag = true;
while(true) {
// 1.提示用户输入发送聊天的内容,并使用变量记录
System.out.println("请"+ (flag? "张三" :"李四") + "输入要发送的聊天内容");
String str = br.readLine();
// 2.判断用户输入的内容是否为 "bye",若是则聊天结束
if ("bye".equals(str)){
System.out.println("聊天结束");
break;
}
// 3. 若不是则将用户输入的内容写入到 文件中
else{
Date d1 = new Date();
SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
ps.println( sdf.format(d1) + (flag? "张三说" :"李四说") + str);
}
flag = !flag;
}
} catch (IOException e) {
e.printStackTrace();
} finally {
// 4.关闭
if(null != ps){
ps.close();
}
if (null !=br){
try {
br.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
9.7、DataOutputStream / DataInputStream类
- 将基本数据类型写入输入流中
- 从输入流中读取基本数据类型的数据
import java.io.*;
public class DataOutputStreamTest {
public static void main(String[] args) {
DataOutputStream dos = null;
try {
//1.创建DataOutputStream 类型的对象 和文件关联
dos = new DataOutputStream(new FileOutputStream("d:/a.txt"));
// 2. 准备一个整数数据66 并写入输入流
int num = 66;
dos.writeInt(num);
System.out.println("写入数据成功");
} catch (IOException e) {
e.printStackTrace();
} finally {
// 3.关闭流对象并释放有关的资源
if(null !=dos){
try {
dos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
DataInputStream dis = null;
try {
//1.创建DataInputStream 类型的对象 和文件关联
dis = new DataInputStream(new FileInputStream("d:/a.txt"));
// 2. 从输入流中读取一个整数并打印
int res = dis.readInt();
System.out.println("读取到的值为:" + res);
} catch (IOException e) {
e.printStackTrace();
} finally {
if(null != dis){
try {
dis.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
9.8、ObjectOutStream / ObjectInputStream
- 在输入流中进行写入对象
- 在输出流中进行读取对象
- 写入的类对象必须实现 Serializable接口 (实现序列化功能)
public class User implements java.io.Serializable {
private static final long serialVersionUID = 7704492997062366869L;
private String userName; // 姓名
private String password; // 密码
private String phoneNum; // 手机号
public User() {
}
public User(String userName, String password, String phoneNum) {
this.userName = userName;
this.password = password;
this.phoneNum = phoneNum;
}
public String getUserName() {
return userName;
}
public void setUserName(String userName) {
this.userName = userName;
}
public String getPassword() {
return password;
}
public void setPassword(String password) {
this.password = password;
}
public String getPhoneNum() {
return phoneNum;
}
public void setPhoneNum(String phoneNum) {
this.phoneNum = phoneNum;
}
}
import java.io.*;
public class ObjectTest {
public static void main(String[] args) {
ObjectOutputStream oos = null;
try {
//1.创建 ObjectOutputStream类型的对象,与文件相关联
oos = new ObjectOutputStream(new FileOutputStream("d:/a.txt"));
// 2. 准备一个User类型的对象,并初始化
User user = new User("LCL","123456","15243245591");
// 3.将整个USER 类型的对象写入输入流
oos.writeObject(user);
System.out.println("写入成功");
} catch (IOException e) {
e.printStackTrace();
} finally {
// 4.关闭,并释放资源
if(null != oos){
try {
oos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
ObjectInputStream ois = null;
try {
//1.创建 ObjectInputStream 类型的对象,与文件相关联
ois = new ObjectInputStream(new FileInputStream("d:/a.txt"));
// 2.从输入流中读取一个对象并打印
Object obj = ois.readObject();
System.out.println("读取到的数据为:" + obj);
} catch (IOException e) {
e.printStackTrace();
} catch (ClassNotFoundException e) {
e.printStackTrace();
} finally {
if(null != ois){
try {
ois.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}
10、JAVA线程
创建方式
- 自定义类继承 Thread 类,并重写run 方法,然后创建该类的对象调用 start() 方法
- 自定义类实现 Runnable接口,并重写 run() 方法,创建该类的对象作为实参来构造 Thread类型的对象
- 使用Thread类型的对象调用 start 方法。
- 线程6种状态 :New(新创建)、Runnable(可运行)、Blocked(被阻塞)、Waiting(等待)、Timed Waiting(计时等待)、Terminated(被终止)、getState(获取当前线程状态)
10.1、启动方式
//创建方式 1
public class SubThreadRun extends Thread {
@Override
public void run() {
for(int i=1;i<=20;i++){
System.out.println("run方法中 i = " + i);
}
}
}
public class SubThreadRunTest {
public static void main(String[] args) {
Thread t1 = new SubThreadRun();
// 启动线程,自动调用虚拟机中的 run 方法
t1.start();
for(int i=1;i<=20;i++){
System.out.println("mian方法中 i = " + i);
}
}
}
// 创建方式2
public class subRunnableRun implements Runnable{
@Override
public void run() {
// 获取子线程的 编号和名字
Thread t2 = Thread.currentThread();
System.out.println("子线程的编号是 " + t2.getId() + "子线程的名字是" + t2.getName());
for(int i=1;i<=20;i++){
System.out.println("run方法中 i = " + i);
}
}
}
public class subRunnableRunTest {
public static void main(String[] args) {
subRunnableRun srr = new subRunnableRun();
Thread t1 = new Thread(srr);
t1.start();
for(int i=1;i<=20;i++){
System.out.println("mian方法中 i = " + i);
}
}
}
// 创建方式 3 :匿名内部类方式实现线程
public class ThreadNoNameTest {
public static void main(String[] args) {
// 匿名内部类的语法格式 :父类/接口类型 引用变量名 = new 父类/接口(){方法重写};
//1.使用继承加匿名内部类的方式创建并启动线程
Thread t1 = new Thread(){
@Override
public void run(){
System.out.println("请问在么?");
}
};
t1.start();
//2.使用实现接口加匿名内部类的方式创建 并启动线程
Runnable ra = new Runnable() {
@Override
public void run() {
System.out.println("不在呀");
}
};
Thread t2 = new Thread(ra);
t2.start();
}
}
10.2、线程等待
public class ThreadJoinTest extends Thread {
@Override
public void run() {
System.out.println("倒计时开始");
for (int i =1;i<10;i++){
System.out.println(i);
try {
Thread.sleep(1000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("新年快乐");
}
}
public static void main(String[] args) {
ThreadJoinTest tjt = new ThreadJoinTest();
tjt.start();
// 主线程开始等待
System.out.println("主线程开始等待");
try {
// 当前线程等待
tjt.join();
// tjt.join(1000); 设置等待时长
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
10.3、守护线程
- 设置守护线程需要在 线程启动前设置
- 一旦线程结束,则守护线程随之结束
public class ThreadDaemonTest extends Thread{
@Override
public void run() {
System.out.println(isDaemon()? "该线程为守护线程" :"该线程不是守护线程"); // 新建线程不是守护线程
for(int i = 0;i<50;i++){
System.out.println("子线程中 i =" + i);
}
}
public static void main(String[] args) {
ThreadDaemonTest tdt = new ThreadDaemonTest();
// 必须在线程启动前设置守护线程,
tdt.setDaemon(true);
tdt.start();
for(int i = 0;i<20;i++){
System.out.println("主线程中 i =" + i);
}
}
}
10.4、同步代码块实现线程同步
public class AccountRunnableTest implements Runnable{
private int balance;
private Demo dm = new Demo();
public AccountRunnableTest() {
}
public AccountRunnableTest(int balance) {
this.balance = balance;
}
public int getBalance() {
return balance;
}
public void setBalance(int balance) {
this.balance = balance;
}
@Override
public void run() {
synchronized (dm) {
// 1.后台查询账户余额
int temp = getBalance();
// 2.模拟取款200元
if(temp>=200){
System.out.println("正在出钞");
temp -= 200;
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("请取走您的钞票");
} else{
System.out.println("账户余额不足");
}
// 3. 模拟最新的账户余额
setBalance(temp);
}
}
public static void main(String[] args) {
AccountRunnableTest account = new AccountRunnableTest(1000);
Thread t1 = new Thread(account);
Thread t2 = new Thread(account);
t1.start();
t2.start();
System.out.println("主线程开始等待");
try {
t1.join();
t2.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("账户最后的余额为:" + account.getBalance());
}
}
class Demo{}
10.5、Thread方式实现同步
public class AccountThreadTest extends Thread{
private int balance;
private static Demo dm = new Demo();
public AccountThreadTest() {
}
public AccountThreadTest(int balance) {
this.balance = balance;
}
public int getBalance() {
return balance;
}
public void setBalance(int balance) {
this.balance = balance;
}
@Override
public void run() {
synchronized (dm) {
// 1.后台查询账户余额
int temp = getBalance();
// 2.模拟取款200元
if(temp>=200){
System.out.println("正在出钞");
temp -= 200;
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("请取走您的钞票");
} else{
System.out.println("账户余额不足");
}
// 3. 模拟最新的账户余额
setBalance(temp);
}
}
public static void main(String[] args) {
AccountThreadTest att1 = new AccountThreadTest(1000);
att1.start();
AccountThreadTest att2 = new AccountThreadTest(1000);
att2.start();
System.out.println("主线程开始等待");
try {
att1.join();
att2.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("账户最后的余额为:" + att1.getBalance());
}
}
10.6、实现同步方式的方式实现全部方法的锁定
// synchronized 锁定方法
// synchronized (this) 等价于 方法锁定
@Override
public synchronized void run() {
// synchronized (this)
synchronized (dm) {
// 1.后台查询账户余额
int temp = getBalance();
// 2.模拟取款200元
if(temp>=200){
System.out.println("正在出钞");
temp -= 200;
try {
Thread.sleep(5000);
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("请取走您的钞票");
} else{
System.out.println("账户余额不足");
}
// 3. 模拟最新的账户余额
setBalance(temp);
}
10.6.1、使用lock锁实现同步
/*
创建锁
private ReentrantLock lock = new ReentrantLock()
lock.lock()
需要锁定的代码
lock.unlock()
*/
10.6.2、wait + notify(线程间的通信)
public class ThreadCommunicateTest implements Runnable{
private int cnt =1;
@Override
public void run() {
while (true){
synchronized (this) {
notify();
if(cnt<=100){
System.out.println("线程" + Thread.currentThread().getName() + "中 cnt =" + cnt);
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
cnt++;
// 打完一个进入 wait()
}else{
break;
}
}
}
}
public static void main(String[] args) {
ThreadCommunicateTest tct = new ThreadCommunicateTest();
Thread t1 = new Thread(tct);
t1.start();
Thread t2 = new Thread(tct);
t2.start();
}
}
10.6.3、生产者消费者模型
public class StoreHouse {
private int cnt = 0; // 记录产品数量
public synchronized void produceProduct(){
notify();
if(cnt<10){
System.out.println("线程" + Thread.currentThread().getName() + "正在生产第" +(cnt+1) +"个商品");
cnt++;
} else{
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public synchronized void consumerProduct() {
notify();
if(cnt>0){
System.out.println("线程" + Thread.currentThread().getName() + "消费第" + cnt +"个商品");
cnt--;
}else{
try {
wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
// 生产者线程,实现不断的生产产品
public class ProduceThread extends Thread{
// 声明一个仓库类型的引用作为成员变量,只为能第哦啊用仓库类中的生产方法,合成复用原则
private StoreHouse storeHouse;
// 确保两个线程共用一个仓库
public ProduceThread(StoreHouse storeHouse){
this.storeHouse =storeHouse;
}
@Override
public void run() {
while (true){
storeHouse.produceProduct();
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
// 生产者线程,实现不断的生产产品
public class ConnsumerThread extends Thread{
// 声明一个仓库类型的引用作为成员变量,只为能第哦啊用仓库类中的生产方法,合成复用原则
private StoreHouse storeHouse;
// 确保两个线程共用一个仓库
public ConnsumerThread(StoreHouse storeHouse){
this.storeHouse =storeHouse;
}
@Override
public void run() {
while (true){
storeHouse.consumerProduct();
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
public class StoreHouseTest {
public static void main(String[] args) {
//创建仓库对象
StoreHouse storeHouse = new StoreHouse();
// 创建线程类对象并启动
ProduceThread t1 = new ProduceThread(storeHouse);
ConnsumerThread t2 = new ConnsumerThread(storeHouse);
t1.start();
t2.start();
}
}
10.7、有返回值的线程
Callable创建线程
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.FutureTask;
public class ThreadCallableTest implements Callable {
@Override
public Object call() throws Exception {
int sum = 0;
for(int i=1;i<=10000;i++){
sum +=i;
}
System.out.println("计算的结果为:" + sum);
return sum;
}
public static void main(String[] args) {
ThreadCallableTest tct = new ThreadCallableTest();
FutureTask ft = new FutureTask(tct);
Thread t1 = new Thread(ft);
t1.start();
Object ob = null;
try {
ob = ft.get();
} catch (InterruptedException e) {
e.printStackTrace();
} catch (ExecutionException e) {
e.printStackTrace();
}
System.out.println("线程的返回结果为:" + ob);
}
}
class CallableTask implements Callable<Integer>{
@Override
public Integer call() throws Exception{
returen new Randow().nextInt();
}
}
// 创建线程池
ExcutorService service = Executors.newFixedThreadPool(10);
// 提交任务,并用Future 提交返回结果
Future<Integer> future = service.submit(new CallableTask());
10.8、线程池
- 本质是通过线程工厂创建线程,默认DefaultThreadFactory, 会给线程池创建线程一些默认值
- 最终还是通过 new Thread() 创建线程
创建线程池方法声明 | 功能介绍 |
---|---|
static ExecutorService newCachedThreadPool() | 创建一个可根据需要创建新线程的线程池 |
static ExecutorService newCachedThreadPool(int nThreads) | 创建一个可重用固定线程数的线程池 |
static ExecutorService newSingleThreadExecutor() | 创建一个只有一个线程的线程池 |
布置任务方法 | 功能介绍 |
---|---|
void execute(Runnable command) | 执行任务和命令,通常用于执行Runnable |
Future submit(Callable task) | 执行任务和命令,通常用于执行Callable |
void shutdown() | 启动有序关闭 |
// 线程池源码
static class DefaultThreadFactory implements ThreadFactory{
DefaultThreadFactory(){
SecurityManager s = System.getSecurityManager();
group = (s!=null)? s.getThreadGroup() :
Thread.currentThread().getThreadGroup();
namePrefix = "pool-" +
poolNumber.getAndIncrement() +
"-thread-";
}
public Thread newThread(Runnable r) {
Thread t = new Thread(group, r,
namePrefix + threadNumber.getAndIncrement(),
0);
if (t.isDaemon())
t.setDaemon(false);
if (t.getPriority() != Thread.NORM_PRIORITY)
t.setPriority(Thread.NORM_PRIORITY);
return t;
}
}
import demo_three.ThreadCallableTest;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
public class ThreadPoolTest {
public static void main(String[] args) {
//1.创建一个线程池
ExecutorService executorService = Executors.newFixedThreadPool(10);
//2.向线程池中布置任务
executorService.submit(new ThreadCallableTest());
//3.关闭线程
executorService.shutdown();
}
}
11、TCP & UDP通信
// 服务端
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintStream;
import java.net.ServerSocket;
import java.net.Socket;
public class ServerStringTest {
public static void main(String[] args) {
ServerSocket ss = null;
Socket s = null;
BufferedReader br =null;
//向客户端发送用
PrintStream ps = null;
try {
//1.创建ServerSocket类型的对象并提供端口号
ss = new ServerSocket(8888);
// 2.等待客户端连接请求,调用 accept方法
System.out.println("请等待客户端的连接请求");
s = ss.accept();
System.out.println("客户端连接成功");
// 3.使用输入输入输出流进行通信
// 实现对客户端发来内容的接收并打印
br = new BufferedReader(new InputStreamReader(s.getInputStream()));
String s1 = br.readLine();
System.out.println("客户端发送来的内容为:" + s1);
ps = new PrintStream(s.getOutputStream());
ps.println("消息收到了");
} catch (IOException e) {
e.printStackTrace();
} finally {
// 4.关闭Socket并释放有关资源
if(null != br){
try {
br.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if(null != s){
try {
s.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if(null != ss){
try {
ss.close();
} catch (IOException e) {
e.printStackTrace();
}
}
if(null != ps){
try {
ps.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
}
}
//客户端
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.io.PrintStream;
import java.net.Socket;
import java.util.Scanner;
public class Client {
public static void main(String[] args) {
Socket s = null;
PrintStream ps = null;
Scanner sc = null;
BufferedReader br =null;
try {
//1.创建Socket类型的对象,并提供服务器的主机名和端口号
s = new Socket("127.0.0.1",8888);
System.out.println("连接服务器成功");
//2.使用输入输出流进行通信
// 实现客户端向服务器发送字符串内容 “hello”
ps = new PrintStream(s.getOutputStream());
System.out.println("请出入要向服务器发送的内容");
sc = new Scanner(System.in);
String str1 = sc.next();
ps.println(str1);
//ps.println("hello");
System.out.println("客户端内容发送成功");
br = new BufferedReader(new InputStreamReader(s.getInputStream()));
String s1 = br.readLine();
System.out.println("服务器发送来的内容为:" + s1);
} catch (IOException e) {
e.printStackTrace();
} finally {
//3.关闭Socket并释放有关资源
if(null != s){
try {
s.close();
} catch (Exception e) {
e.printStackTrace();
}
}
if(null != ps){
try {
ps.close();
} catch (Exception e) {
e.printStackTrace();
}
}
if(null != br){
try {
br.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
}
}