java学习笔记
JAVA学习
第一章 基础常识
1.1基础常识
1.软件开发
软件:计算机数据和指令的集合,系统软件和应用软件。
2.人机交互方式
图形化界面(一般)。
命令行方式:控制台输入指令。
3.常用DOS命令
dir:列出当前文件及文件夹
md:创建目录
rd: 删除目录
cd:进入指定目录
cd..:退回上级目录
cd\:退回根目录
del:删除文件
exit:推出dos命令
1.2 java语言概述
1.计算机语言
人与计算机交流方式:java、c、c++
2.面向对象
能够更好的在抽象层面来分析问题。
3.java解释型语言
java程序编译为字节码格式,在任何系统的解释器中运行。
1.3 java语言运行机制及以运行过程
1. java特点
(1)面向对象
(2)健壮性、完善性
(3)跨平台性:一次编写,处处运行。只需要安装java虚拟机 (JVM),由JVM负责java程序在该系统中的运行。
2.java两种核心机制
(1)java虚拟机(Java Virtal Machine):JVM
(2)垃圾收集机制(Garbage Collection):GC
3.跨平台性(因为JVM)

4.核心机制——java虚拟机
(1)JVA是一个虚拟的计算机,具有指令集并使用不同的存储区域。负 责执行指令,管理数据、内存、寄存器。
(2)java虚拟机机制屏蔽了底层运行平台的差别。

5.核心机制——垃圾回收
(1)c、c++:程序员手动编代码回收,(有点:能够在内存不使用时快 速回收,准确高效;缺点:容易失误出现bug)
(2)java: 垃圾回收是自动的,开了一个线程自动检测不用的内 存去回收(有点:自动的不会忘掉;缺点:回收不及时)
1.4 jdk、jre、jvm的关系
1. 什么是jdk、jre
(1)jdk:Java开发工具包,包括jre;其中的开发工具:编译工具 (javac.exe)打包工具(jar.exe)等
(2)jre:java运行环境,包括java虚拟机(JVM),和Java程序所需的 核心类库等,如果想要运行一个开发好的Java程序,计算机中只 要安装JRE即可。
简而言之:jdk开发,jre运行。
2 jdk、jre、jvm的关系
JDK( JRE (JVM) )
1.5开发体验——HelloWorld
public class Test{
public static void main(String[] args){
System.out.print("hello world");
}
}
DOS命令运行:
编译:javac Test.java
运行:java Test
1.6 注释
1.特性:
提高代码阅读性,调试程序的重要方法
2.注释类型
单行://
多行:/* */
文档注释(Java特有):
/**
*文档注释
*这是一个打印Hello World的类
*@author ahd
*version
*/
第二章 java基本语法
2.1关键字
1.关键字
定义:被Java赋予特殊含义,用作专门用途的字符串(单词)
特点:都是小写

2.保留字
现在还没使用,以后的版本可能会使用为关键字:byValue、cast、 future、generic、inner、operator、outer、rest、var、goto、const;
2.2 标识符
1. 标识符
可以自己取名的地方都叫标识符。
2.规范
(1)26个英文字母,0-9,_或$组成;
(2)数字不可以开头;
(3)不可以单独使用关键字和保留字,可以包含;
(4)严格区分大小写
(5)不包含空格
(6)尽量有意义
2.3 java中的名称命名规范
1.Java中的命名规范
(1)包名:多单词组成时所有都小写;
(2)类名、接口:多单词组成时,所有首字母都大写;
(3)变量名、方法名:第一个单词首字母小写,第二个往后 首字 母大写:xxxYyyZzz;
(4)常量名:所有都大写,多单词用下划线链接 XXX_YYY_ZZZ
2.4 变量概念(和C一样)
1.变量格式
数据类型 变量名 = 变量的值
2.变量的概念
(1)内存中的一个储存区域;
(2)必须先声明后使用;
(3)变量是通过变量名来访问的。
2.5 变量的分类

1. long 类型
long L = 3l 要在后面跟上 l (字母)
2. float类型
floa f = 1.22f 跟上字母 f
2.6 运算符
1.算数运算符
对负数取模,可以忽略负号,被模数是负数不可忽略。
2.逻辑运算符
(1)^ 异或:两个里面一个成立一个不成立;
(2)“&”和“&&“区别:单个时,左边无论真假,右边都参与运算;双数 时,左为真时,右参与,否则不参与;
”|“和“||” :单个同理;双数时左边为真,右边不参与运算。
3. 位运算符

(1)位运算符是直接对二进制进行运算。
(2)左移:m<<n = m*2的n次方
3<<2 = 3 * 2 * 2 = 12
右移:乘以2的-n次方
(3)无符号右移:
正数:与右移一致
负数:看图

(4)与或运算![与或运算]

(5)异或运算

(6)反码

4. 三目运算符
(1)格式:

int i = 0;
int k = i > 0 ? 1 : 0;
5.运算符的优先级

2.7程序流程控制
1. 顺序结构
2.分支结构
3.循环结构
2.8数组
1.一维数组
int[] ii;
int a[];
//动态初始化:数组声明且为其分配空间,与复制操作分开进行;
int[] b = new int[4];
b[0] = 1;
b[1] = 2;
b[2] = 3;
//静态初始化:
int[] c = new int[]{1,2,3,4};
2.二维数组
int[] ii0 = new int[2][3];
第三章 面向对象编程
3.1面向对象与面向过程
1.面向对象三大特征
封装、继承、多态
2.类class
(1)属性:对应类中的成员变量
(2)行为:对应类中的成员方法
3.思想概述
(1)类=汽车设计图;对象=实实在在的汽车
(2)对象=实实在在的汽车
(3)面向对象程序设计的重点时类的设计
(4)定义类其实就是定义类中的成员(成员变量(零部件)和方法(运 行))
3.2类
类成员:属性、方法、构造器、代码块、内部类。
1.类的写法
public class Animal {
String name;//动物名称
int eyes;
int legs;
public int getEyes(){
return eyes;
}
public void eat(String food){
System.out.println("动物吃的是:"+food);
}
public void move(String moveType){
System.out.println("此种动物的移动方式是:"+moveType);
}
}
2.类的实例化
public class Test {
public static void main(String[] args){
//实例化动物类
Animal animal = new Animal();
animal.name = "小狗";
animal.eyes = 2;
animal.legs = 4;
System.out.println("动物是"+animal.name);
animal.eat("骨头");
animal.move("爬");
int a = animal.getEyes();
System.out.println(a);
}
}
3.类的属性
(1)private//私有变量,只能在在自己的类里面使用。
public//公有变量。
(2)成员变量:
类变量:以static修饰,静态;
实例变量:在类实例化位对象之后才能使用。
(3)局部变量:
形参、方法局部变量、代码块局部变量。
4.类的方法
public class Student {
public String name;
public int age;
public String course;
public String interest;
/**
显示学生个人信息
*/
public void showIofo(){
System.out.println("姓名:"+name);
System.out.println("年龄:"+age);
System.out.println("课程:"+course);
System.out.println("兴趣:"+interest);
}
}
5.匿名对象
不定义对象的句柄,直接调用这个对象的方法
new Person().shout();
使用场景:只调用一次就不再调用了。
经常将匿名对象作为实参传递给一个方法使用。
6.面向对象思想“落地”法则(1)
(1)关于类的设计,即设计类的成员:属性和放法;
(2)类的实例化,即创建类的对象;
(3)通过“对象.属性”、“对象.方法” 执行。
7.方法的重载
//多个相同名称方法在一个类中共存,参数肯定不一样
public int add(int x,int y){
return x+y;
}
public double add(int x,double y){
return x+y;
}
public int add(int x,int y,int z){
return x+y+z;
}
8.方法的参数传递
(1)java里方法的参数传递方式只有一种:值传递。即将实际参数值的副 本传入方法内
(2)JVM的内存模型

(3)方法的参数传递
a.如果方法的形参是基本数据类型,那么实参(实际的数据)向形参传递 参数时,就是直接传递值,把实参的值复制给形参。
b.如果方法的形参是对象,那么实参(实际的对象),向形参传递参数 时,也是把值给形参,这个值是实参在栈内存中的值,也就是引用对象 在堆 内存中的地址。
基本数据类型都是保存在栈内存中,引用对象在栈内存中保存的是引用 对象的地址,那么方法的参数传递是传递值(是变量在栈内存的值)。
3.3面向对象特征之一:封装和隐藏
先把属性设置位私有的(private),通过set和get方法来设置获取属性。
3.4四种访问权限修饰符

在一个java文件中可以写多个class,但是只有一个public,其他的只能缺省。
3.5类的成员之三:构造器(构造方法)
1.构造器特征
(1)语法格式:
修饰符 类名(参数列表){
初始化语句;
}
public Animal(){legs = 4;}
(2)new对象实际上就是调用类的构造方法。
2.构造器的重载
(1)构造器也叫构造方法
public class Person(){
public Person(int a){}
public Person(String n){}
public Person(){int a,String n}
}
(2)重载意义:为了方便灵活的创建出不同需求的对象,相当于提供了多个初始化new对象的模板。
3.6关键字——this
public class Person(){
public Person(){
}
public Person(int age){
this.age = age;
}
public Person(int age,String name){
this.age = age;
this.name = name;
}
int age;
String name;
public void setName(String name){
this.name = name;
}
public void setName(String name){
this.setName(name);
}
}
3.7 JavaBean
(1)JavaBean是一种Java语言写成的可重用组件;
(2)所谓javaBean,是指符合如下标准的Java类:
a.类是公共的;
b.有一个无参的公共构造器;
c.有属性,属性一般是私有的,且有对应的get、set方法。
(3)用户可以使用JavaBean将功能、处理、值、数据库访问和其他任何可以用Java代码创造的对象打包,其他开发者可以用其他的程序或者应用来使用些对象。用户可以认为javabean提供了一种随时随地的复制和粘贴的功能,而不用关心任何改变。
第四章 高级类特性1
4.1 面向对象特征之二——继承
1.继承
(1)把共性的东西抽取出来形成父类,实际需求的子类在继承父类的基础 上写自己特有的代码即可。
(2)继承的出现提高代码复用性;
让类与类之间产生了关系,提供了多态的前提;
不要仅为了获取其他类中某个功能而去继承(逻辑关系)。
(3)子类不是父类的子集,是扩展。
(4)子类不能直接访问父类中的私有变量,可以用get set方法;
(5)Java类只能单继承(一个子类只能继承一个父类)。
public class Person{
String name;
int age;
int sex;
public void info(){
System.out.println(this.name);
System.out.println(this.age);
System.out.println(this.sex);
}
}
public class Student extends Person{
String school;
public void info(){
System.out.println(this.name);
System.out.println(this.age);
System.out.println(this.sex);
System.out.println(this.school);
}
}
2.方法的重写(区别重载)
(1)子类重写父类的方法只是重写方法体的代码,名称参数不变;
(2)如果父类是public的,子类就不能使用缺省以下的;
(3)重写和非重写的方法必须同时是static或同时是非static的;
(4)子类方法抛出的异常不能大于父类被重写方法的异常。
4.2 关键字——super
1.概念
(1)使用super,子类可以调用子类之上的所有父类层级。
public class Person{
String name;
int age;
int sex;
public void info(){
System.out.println(this.name);
System.out.println(this.age);
System.out.println(this.sex);
}
}
public class Student extends Person{
String school;
public void info(){
System.out.println(this.name);
System.out.println(this.age);
System.out.println(this.sex);
System.out.println(this.school);
}
}
public class test extends Student{
super.name = "ahd"; //调用父类的父类
super.school = "shenhang"; //调用父类;
}
2.this和super的区别

4.3简单类对象的实例化过程

4.4面向对象特征之三——多态
1.体现概念
(1)方法的重载(overload)和方法的重写(overwrite);
(2)对象的多态性——可以直接应用在抽象类和接口上。
2.引用变量的两个类型
(1)编译时类型和运行时类型:编译时类型由声明该变量时使用的类型决定,运行时类型由实际赋给改变量的对象决定;
(2)若编译时类型和运行时类型不一致,就出现多态。
3.多态小结
(1)前提:
a. 需要存在继承或者实现关系;
b. 需要有覆盖操作。
(2)成员方法:
a. 编译时:要查看引用变量所属的类中是否有所调用的方法;
b. 运行时:调用实际对象所属的类中的重写方法。
(3)成员变量:
不具备多态性,只看引用变量所属的类。
4.5 Object类
1. 概念
(1)Objec类是所有Java类的根父类(基类)。
(2)如果在类的声明中未使用extends关键字指明其父类,则默认父类为Object类。
2. 主要方法

3. toString方法
(1)父类Object的toString方法就是输出当前对象的内存地址,如果想要输出类的其他信息,重写toString方法。
4.6 对象类型转换
1.概念
(1)对Java对象的强制类型转换成为造型;
(2)从子类到父类的类型转换可以自动进行;
(3)从父类到子类的类型转换必须通过造型(强制类型转换)实现;
(4)无继承关系的引用类型间的转换时非法的。
public class Test{
public static void main(String[] args){
//Person是Student的父类,从子类到父类的类型转换是自动的
Student s = new Student();
Person p = s;
//从父类到子类的类型转换必须强制
Person p = new Person();
Student s = (Student) p;
}
}
2.对比基础数据类型

4.7 ==操作符与equal方法
1.==概念
(1)== :两个变量相等,即位true;
(2)用“==”进行比较时,符号两边必须兼容(可以自动转换的基本数据类型除外);
(3)用==比较引用类型时,必须指向同一个对象,才返回true。
2. String对象的创建

4.8包装类
1.概念
(1)针对八种基本定义相应的引用类型——包装类(封装类);
(2)有了类的特点,就可以调用类中的方法。

2.基本类型与字符串相互转换
(1)基本类型转字符串
int i = Integer.parseInt("123");
float f = Float.parseFloat("0.41");
boolean b = Boolean.parseBoolean("false");
(2)字符串转基本类型
String istr = String.valueOf(i);
String fstr = String.valueOf(f);
String bstr = String.valueOf(true);
3.作用
主要应用字符串与字符串相互转化。
第五章 高级类特性2
5.1 关键字static
1.概念
(1)静态 ,类变量,不用实例化直接通过 类名 . 属性名 可以使用,是类的一部分,被所有这个类的实例化对象所共享。也可以叫做静态变量。
(2)同样类属性、类方法也可以使用。
2.设计思想
(1)有些类属性和方法不想因为对象不同而频繁通过new对象方式去调用,就写static。
(2)类方法,也就是静态方法在工具类中使用的频繁。
/**
*工具类
*判断是否为一个空字符串
*/
public class Utils{
public static boolean isEmpty(String s){
bollean flag = false;
if (s != null && !s.equal("")){
falg = true;
}
return flag;
}
}
3.特点
(1)类加载之后,静态方法或者属性就能用了,类名 . **;
(2)类变量,这种可以被所有的实例化对象共享的属性,使用要慎重,只要修改,所有的都要变动。
5.2 单例设计模式
1. 设计模式
设计模式就是在我们实际编程的过程中,逐渐总结出的一些解决问题的套路。
2.单例
(1)只有一个实例(实例化对象);
(2)在整个软件系统的运行过程中,这个类只被实例化一次,以后不管在哪都只调用这一个实例。
(3)例如实例化对象的创建要消耗大量的时间和资源。
(4)使用单例模式解决什么问题?一般都是new对象太费劲,或者频繁的new新的对象没有必要。
3.单例模式——饿汉式
类加载之后,还没有人调用的时候,就先new好一个对象,以后不论谁来调用getInstance方法,都是直接返回之前new好的对象。
/**
*饿汉式
*/
public class Single{
//构造方法私有化,调用这个类的人就不能直接使用new来创建对象
private Single(){
}
//私有的Single类型的类变量
private static Single single = new Single();
public static Single getInstance(){
return single;
}
}
4.单例模式——懒汉式
最开始对象是null,知道有第一个人调用,才new一个对象,之后所有调用都用这个对象。
/**
*懒汉式
*/
public class Single1{
//构造方法私有化,调用这个类的人就不能直接使用new来创建对象
private Single1(){
}
//私有的Single类型的类变量
private static Single s1 = null;
public static Single getInstance(){
if(s1 == null){
s1 = new Single1();
}
return s1;
}
}
5.3类的成员之四:初始化块(代码块)
1.非静态代码块(没有static修饰)
(1)可以有输出语句;
(2)可与对类的属性声明进行初始化操作;
(3)可以调用静态和非静态的变量或方法;
(4)若有多个非静态代码块,从上到下依次执行;
(5)每次创建对象的时候,都会执行一次,且先于构造器执行。
2.静态代码块(用static修饰)
(1)可以有输出语句;
(2)可与对类的属性声明进行初始化操作;
(3)不可以调用非静态的属性和方法;
(4)若有多个静态代码块,从上到下依次执行;
(5)静态代码块的执行要先于非静态代码块;
(6)静态代码块只执行一次。
3.作用
匿名内部类没有构造方法,只能用代码块代替构造方法初始化。
5.4关键字:final(最终)
1.特性:
(1)final标记的类不能被继承;
(2)final标记的方法不能被子类重写;
(3)final标记的变量(成员变量或局部变量)即称为常量,名称大写,且只能赋值一次;
(4)final和static同时修饰成为全局变量。
2.总结
final表示最终,可以修饰变量、方法、类。
5.5抽象类(abstract class)
1.抽象化处理
就是不写具体的实现。
2.abstract(抽象)
(1)用abstract修饰一个类叫抽象类
(2)用abstract修饰一个方法叫做抽象方法。
抽象方法只有声明,没有方法的实现,以分号结束
abstract int abstractMethod(int a );
(3)含有抽象方法的类必须声明为抽象类;
(4)抽象类不能被实例化。抽象类是用来被继承的,抽象类的子类必须重写父类的抽象方法。若没有全部重写,则还是为抽象类。
(5)不能用abstract修饰属性、私有方法、构造器、静态方法、final的方法。
3.抽象类应用
抽象类是用来模型化那些父类无法确定全部实现,由子类提供具体实现的对象的类。
4.模板方法设计模式
抽象类就是多个子类的通用模板,抽象类就像一个大纲,里面的每个抽象方法每个章节的标题,子类去根据这些标题写出每个章节。
5.6接口
1.定义
有时必须从几个类中派生出一个子类,继承它们所有的属性和方法。但是java中不支持多继承,有了接口就可以得到多继承的效果。
2.特点
(1)接口是一种特殊的抽象类,这种抽象类中只包含常量和方法的定义,而没有变量和方法的实现。
(2)一个类可以实现多个接口,接口也可以继承其他接口。
(3)接口中所有成员变量都默认是有 public static final 修饰的。
(4)所有的方法都是由 public abstract 修饰。
(5)裂口没有构造器。
(6)接口可以继承,接口采用多层继承制。
(7)如果一个类即继承父类又实现接口,先继承、后实现。
public class TestinImpl2 extends Person implements TestIn,TestIn1{
@Overdide
public void Test(){
}
@Overdide
public void Test1(){
}
}
3.实现
public interface TestIn{
int ID = 1;
void test();//public abstraact void test();
}
public interface TestIn1{
void test1();//public abstraact void test1();
}
/**
*子类继承父类,只能继承一个父类
*类可以实现多个接口,多个接口用 ,分隔
*/
public class TestInImpl implements TestIn,TestIn1{
@Overdide
public void Test(){
}
@Overdide
public void Test1(){
}
}
4.接口中存在的问题

5.总结
抽象类是对于一类事物的高度抽象,其中既有属性也有方法,接口是对方法的抽象,也就是对一系列动作的抽象。
当需要对一类事物抽象的时候,应该是使用抽象类,好形成一个父类,
当需要对一系列的动作抽象时,就使用接口,需要使用这些动作的类就去实现相应的接口。
6.工厂方法(FactoryMethod)
通过面向对象的手法,将所要创建的具体对象的创建工作延迟到了子类,从而提供了一种扩展的策略。类中new一个对象很容易,但是时机很关键,工厂模式就是解决的这个问题。
5.7类成员之五:内部类(Inner class)
1.创建
public class Test{
int i;
public int z;
class A{
int i;
public void setTestFiled(){
Test.this.i = 1;
Test.this.z = 2;
}
public void set(){
this.i = 10;
}
}
public void setInfo(){
new A().setTestFiled();//外部类要使用自己的内部类的方法,要先new内部类的对象。
}
public void showInfo(){
System.out.println(this.i);
System.out.println(this.z);
}
}
2.特性
(1)可以声明为fianl的;
(2)和外部类不同,内部类可以声明为private或protected;
(3)内部类可以声明为static的,但此时就不能再使用外层类的非static的成员变量。
(4)可以声明为abstract,可以被继承;
(5)非static的内部类中的成员不能声明为static,只有在外部类或者static的内部类才可以声明static成员。
3.作用
内部类主要是解决java不能多重继承的问题
/**
*A要同时继承B和C
*/
class A{
public void testB(){
new InnerB().testb;
}
public void testB(){
new InnerB().testb;
}
private class InnerB extends B{
@override
public void testb(){
System.out.println("这是重写之后的testB方法");
}
}
private class InnerC extends C{
@override
public void testC(){
System.out.println("这是重写之后的testC方法");
}
}
}
class B{
public void testb(){
}
}
class C{
public void testc(){
}
}
第六章 异常处理
6.1 java异常
1.种类
(1)Error:JVM系统内部错误,资源耗尽等严重情况。
(2)Exception:其他因编程错误或偶然的往外在因素导致的一般性问题。
2.异常处理机制(抓抛机制)
(1)捕获异常
int i = 0;
try{//用try{}来括住一段有可能异常的代码段
System.out.println(3/i);
}catch(Exception e){//当不知道捕获的是什么类型的异常时,可以直接使用所有异常的父类Exception
System.out.println(e.getmessage());
}
(2)抛出异常
public class Test1 {
public static void main(String[] args){
B b = new B();
try {
b.test();
}catch (Exception e){
e.printStackTrace();
}
}
}
class B{
int i;
public void test() throws Exception{//可以使用throws在代码这抛出异常,在调用方法去捕获异常
B b = null;
System.out.println(b.i);
}
}
【注意】
子类重写父类的方法时,抛出的异常范围不能大于父类。
第七章 集合
7.1 java集合概述
1.概述
java集合类存放在java.util包中,是一个用来存放对象的容器。
(1)集合只能存放对象
(2)集合存放的是多个对象的引用,对象本上还是存放在堆中。
(3)集合可以放不同类型、不限数量的数据类型。
2.体系
java集合分为Set、List、Map 三大体系,
(1)Set:无序、不可重复的集合
(2)list:有序、可重复的集合
(3)Map:具有映射关系的集合
3.HashSet集合
(1)HashSet按照Hash算法来存储集合中的元素,因此具有很好的存取和查找功能。
(2)特点:
· 不能保证元素的排列顺序
· 不可重复
· HashSet不是线程安全的
· 集合元素可以是 null
(3)HashSet集合是根据对象的hashCode值决定该对象在HashSet中的存储位置。
【注】hashCode就是对象在hash表中的位置
(4)增删改
import java.util.HashSet;
import java.util.Set;
public class HashTest {
public static void main(String[] args){
Set set = new HashSet();
set.add(1);//增加元素
set.add("a");
System.out.println(set);
set.remove(1);移除元素
System.out.println(set);
System.out.println( set.contains("a"));
set.clear();//清空集合
System.out.println(set);
System.out.println(set.size());//获取元素的个数
}
}
(5)遍历
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
public class HashTest {
public static void main(String[] args){
Set set = new HashSet();
set.add("a");
set.add("b");
set.add("c");
set.add("d");
System.out.println(set);
//使用迭代器遍历集合
Iterator it = set.iterator();
while(it.hasNext()){
System.out.println(it.next());
}
//for each迭代集合
for(Object obj : set){//把set的每一个值取出来赋值给obj直到循环set的所有值
System.out.println(obj);
}
}
}
(6)泛型
Set<String> set = new HashSet<String>();//比如指定String类型,那么这个集合就不能存其他类型了
4.TreeSet集合
(1)TreeSet是SortSet接口的实现类,TreeSet可以确保集合元素处于排序状态。
(2)TreeSet的两种排序方式:自然排序和定制排序,默认为自然排序。
(3)自然排序:TreeSet会调用集合元素的compareTo(Object obj)方法来比较元素之间的大小关系,然后将集合元素按升序排列
· 如果this(当前元素)>obj(之后元素),返回1
· 如果this > obj,返回-1
· 如果this > obj,返回0,认为相等
【注】必须放入同样的对象,可以用泛型来限制。
(4)定制排序(实现Comparator接口中的Compare方法)
import java.util.Comparator;
import java.util.Iterator;
import java.util.Set;
import java.util.TreeSet;
public class TreeSetTest {
public static void main(String[] args){
Person p1 = new Person("张三",23);
Person p2 = new Person("李四",20);
Person p3 = new Person("王五",16);
Person p4 = new Person("Lusy",29);
Set<Person> set = new TreeSet<Person>(new Person());
set.add(p1);
set.add(p2);
set.add(p3);
set.add(p4);
for(Person p : set){
System.out.println(p.name+" "+p.age);
}
}
}
class Person implements Comparator<Person>{//把person存到TreeSet中,按照年龄排序
int age;
String name;
public Person(){}
public Person(String name,int age){
this.name = name;
this.age = age;
}
@Override
public int compare(Person o1, Person o2) {
if(o1.age>o2.age){
return 1;
}else if(o1.age<o2.age){
return -1;
}else {
return 0;
}
}
}
5.List集合
List是接口,实现类是ArrayList。
(1)特点:
· 允许使用重复元素,可以通过索引来访问指定位置的集合元素
· List默认按元素的添加顺序设置元素的索引
· List集合里添加了一些根据索引来操作集合元素的方法
(2)操作元素的方法
import java.util.ArrayList;
import java.util.List;
public class ListTest {
public static void main(String[] args){
List<String> list = new ArrayList<String>();
list.add("b");
list.add("d");
list.add("c");
list.add("a");
list.add("b");
System.out.println(list);
System.out.println(list.get(2));//通过所以呢来访问元素
list.add(1,"e");//在指定索引位置插入数据,其他后移
System.out.println(list);
List<String> l = new ArrayList<String>();
l.add("123");
l.add("456");
list.addAll(2,l);//在指定位置插入集合
System.out.println(list);
System.out.println(list.indexOf("a"));//获取指定元素在集合中第一次出现的下标
System.out.println(list.lastIndexOf("b"));//获取指定元素在集合中最后一次出现的下标
list.remove(1);//移除指定下标的元素
System.out.println(list);
list.set(0,"bb");//修改指定位置的元素
System.out.println(list);
List<String>sublist = list.subList(2,4);//截取集合元素,左包含右不包含
System.out.println(sublist);
}
}
5.Map集合
(1)定义:Map用于保存有映射关系的数据,Map有两组值(Key,Value),都可以是引用数据,同一个Map对象里面的key不允许重复,Key和Value之间存在单向的一对一关系。
(2)Map接口和HashMap
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
public class MapTest {
public static void main(String[] args){
Map<String,Integer> map = new HashMap<String, Integer>();
map.put("b",1);//添加数据
map.put("c",2);
map.put("e",2);
System.out.println(map);
System.out.println(map.get("c"));//根据Key取值
map.remove("c");//根据Key移除键值对
System.out.println(map);
System.out.println(map.containsKey("b"));//判断当前集合是否包含指定key
System.out.println(map.containsValue(2));//判断是否有指定的value
Set<String>keys = map.keySet();//获取map集合中所有的key
map.values();//获取map集合中所有的value
//遍历map集合通过map.keySet();
for(String key : keys){
System.out.println("key:"+key+"value:"+map.get(key));
}
//通过map.entrySet();
Set<Map.Entry<String,Integer>> entry = map.entrySet();
for(Map.Entry<String,Integer> en : entry){
System.out.println("key:"+en.getKey()+"value:"+en.getValue());
}
}
}
(3)Hash和Hashtable
区别:Hashtable是线程安全的Map实现,但HashMap是线程不安全的;
Hashtable不允许使用null作为key和value的,而HashMap可以。
(4)TreeMap:与TreeSet相似。
7.2操作集合的工具类
1.Collections定义
Collections是一个操作Set、List和Map等集合的工具类。
2.方法
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;
public class Test01 {
public static void main(String[] args){
List<String> list = new ArrayList<String>();
list.add("b");
list.add("cd");
list.add("ca");
list.add("a");
list.add("1");
System.out.println(list);
Collections.reverse(list);//反转list
System.out.println(list);
Collections.shuffle(list);//随机排序
System.out.println(list);
Collections.sort(list);//list字典升序排序
System.out.println(list);
Collections.swap(list,0,4);//交换两个元素
System.out.println(list);
System.out.println(Collections.max(list));//输出最大
System.out.println(Collections.min(list));//输出最小
System.out.println(Collections.frequency(list,"a"));//指定元素出现的次数
Collections.replaceAll(list,"a","aa");//新的值代替旧值
Student s1 = new Student(14,"张三");
Student s2 = new Student(13,"李四");
Student s3 = new Student(12,"王五");
Student s4 = new Student(11,"小六");
List<Student> stus = new ArrayList<Student>();
stus.add(s1);
stus.add(s2);
stus.add(s3);
stus.add(s4);
System.out.println("------------------");
Collections.sort(stus,new Student());
for(Student stu : stus){
System.out.println(stu.name+","+stu.age);
}
}
}
class Student implements Comparator<Student>{
int age;
String name;
public Student(){
}
public Student(int age,String name){
this.age = age;
this.name = name;
}
@Override
public int compare(Student o1, Student o2) {//根据年龄升序排列
if(o1.age>o2.age){
return 1;
}else if(o1.age<o2.age){
return -1;
}else {
return 0;
}
}
}
第八章 泛型
8.1泛型的使用
1.泛型类
public class testfanxing {
public static void main(String[] args){
A<String> a1 = new A<String>();//在new A的对象指定泛型的类型string
a1.setKey("xxx");//对象使用setKey(T key)方法时参数类型就是String
String s = a1.getKey();//返回值,就由new对象确定返回值是string
}
}
/**
* 此次的泛型T可以任意取名
* 一般使用大写的T,意为type
* */
class A<T>{
private T key;
public void setKey(T key){
this.key = key;
}
public T getKey(){
return this.key;
}
}
2.泛型接口
未传入泛型实参时,与泛型类的定义相同,在声明类的时候,需要将泛型的声明也一起加到类中
public class testinterface {
public static void main(String[] args){
B1<Object> b1 = new B1<Object>();//必须指定泛型
B2 b2 = new B2();//不用指定泛型
}
}
interface IB<T>{
T test(T t);
}
class B1<T> implements IB<T>{
@Override
public T test(T t) {
return null;
}
}
/**
* 如果实现接口时指定接口的泛型的具体数据类型
* 这个实现接口所有的位置都要泛型替换实际的具体数据类型
* */
class B2 implements IB<String>{
@Override
public String test(String s) {
return null;
}
}
3.泛型方法
方法也可以被泛型化,不管此时定义在其中的类是不是泛型化的。在反省方法中可以定义泛型参数,此时,参数的类型就是传入数据的类型。
class Cc<E>{
private E e;
public static <T> void test3(T t){
//在静态方法上不能使用类定义的泛型,如果要使用,只能静态方法自己定义泛型
}
public <T> void test(T s){//无返回值的泛型方法
System.out.println(this.e);//在类上定义的泛型,可以在普通方法上使用
T t = s;
}
public <T> T test1(T s){//有返回值的泛型方法
return s;
}
public <T> void test2(T... strs){//形参为可变参数的泛型方法
for(T s : strs){
System.out.println(s);
}
}
}
4.通配符
/**
*不确定集合中的元素具体的数据类型
*使用?表示所有类型
*@param list
*/
第八章 枚举和注解
8.1枚举类
1.使用enum定义枚举类
public class Testmeiju {
public static void main(String[] args){
//Season.SPRING,这段执行就是获取一个Season的对象
Season spring = Season.SPRING;
spring.showInfo();
Season spring1 = Season.SPRING;
//每次执行Season.SPRING获得是相同的对象,枚举类中的每个枚举都是单例模式的
System.out.println(spring.equals(spring1));
}
}
enum Season{
SPRING("春天","还好"),//此处相当于调用有参数的私有造方法
SUMMER("夏天","热热"),
AUTUMN("秋天","爽爽"),
WINTER("冬天","冷冷");
private final String name;
private final String desc;
Season(String name,String desc) {
this.name = name;
this.desc = desc;
}
public void showInfo(){
System.out.println(this.name+":"+this.desc);
}
}
2.枚举类实现接口
与普通接口的实现相同
8.2注解(Annotation)
1.定义
代码里的特殊标记,这些标记可以在编译,类加载,运行时被读取,并执行相应的处理。
第九章 IO流
9.1 java.io.File类的使用
1.IO原理及流的分类
(1)文件流
数据流的读写都是基于文件的操作。
(2)缓冲流
数据流的读写都是基于内存的操作。
9.2 File类
1.作用
file只能操作文件,不能操作文件内容。
2.方法
import java.io.File;
public class testFile {
public static void main(String[] args){
File f = new File("F:\\java学习\\testfile\\tt.txt");//这个对象f就是tt.txt文件
//获取文件名
System.out.println(f.getName());
//获得路径
System.out.println(f.getPath());
//获得绝对路径
System.out.println(f.getAbsolutePath());
//返回一个用当前文件的绝对路径构建的file对象
System.out.println(f.getAbsoluteFile());
//返回当前文件或者文件夹的父级路径
System.out.println(f.getParent());
//重命名
f.renameTo(new File("F:\\java学习\\testfile\\tt1.txt"));
//判断是否存在
File f1 = new File("F:\\java学习\\testfile");
System.out.println(f1.exists());
//是否可读写
File f2 = new File("F:\\java学习\\testfile\\tt1.txt");
System.out.println(f2.canRead());
System.out.println(f2.canWrite());
//判断当前的file的对象是否为文件
System.out.println(f2.isFile());
//判断是否为文件夹
System.out.println(f2.isDirectory());
//获取最后修改时间
System.out.println(f2.lastModified());
//返回文件长度,单位是字节数
System.out.println(f2.length());
File f3 = new File("F:\\java学习\\testfile\\tt2.txt");
//创建文件
if(!f3.exists()){
try {
f3.createNewFile();
}catch(IOException e){
e.printStackTrace();
}
}
//删除文件
f3.delete();
//创建单机目录
File f4 = new File("F:\\java学习\\testfile\\cc");
f4.mkdir();
//创建多层目录
File f5 = new File("F:\\java学习\\testfile\\a\\b\\c");
f5.mkdirs();
File f6 = new File("F:\\java学习\\testfile");
String[] f11 = f6.list();//返回文件夹的子集
for(String s : f11){
System.out.println(s);
}
}
}
9.3 Java IO原理
1.输入input
读取外部数据到程序(内存)中。
2.输出output
将程序(内存)数据输出到磁盘、光盘等储存设备中。
3.流的分类
(1)数据单位不同:字节流(8 bit),字符流(16 bit)。
(2)流向不同:输入流,输出流。
(3)角色不同:节点流,处理六。

4.文件字节流
(1)文件字节输入流
import java.io.FileInputStream;
public static void testFileIutputStream(){
try {
FileInputStream in =new FileInputStream("F:/java学习/testfile/tt1.txt");
byte[] b = new byte[20];//设置一个byte数组来接收读取的文件内容
int len = 1;//设置一个读取数据的长度
//in.read(b) 方法的返回值是返回读取的数据长度,当返回的是-1时,表示读完。
while((len = in.read(b)) != -1){
//string(b,0,len)参数1是缓冲数据的数组,参数2是从数组的哪个位置开始转化字符串,参数3是总共转化了几个
System.out.println(new String(b,0,len));
}
in.close();//注意,流在使用完毕之后一定要关闭
}catch (Exception e){
e.printStackTrace();
}
}
}
(2)文件字节的输出流
public static void testFileOutputStream(){
try {
//指定x向tt4中输出
FileOutputStream out =new FileOutputStream("F:/java学习/testfile/tt4.txt");
String str = "dffsffds";
//把数据写到内存
out.write(str.getBytes());
//把内存中的数据刷写到硬盘
out.flush();
out.close();
} catch (Exception e) {
e.printStackTrace();
}
}
(3)复制文件到指定文件夹
/**
* 复制文件到指定位置
* @param inPath 源文件的路径
* @param outPath 复制到的文件夹的位置
*/
public static void copyFile(String inPath,String outPath){
try {
//读取的源文件
FileInputStream in =new FileInputStream(inPath);
//复制到文件
FileOutputStream out =new FileOutputStream(outPath);
byte[] b = new byte[100];
int len = 0;
while((len = in.read(b)) != -1){
//参数1:是写的缓冲数组,参数2:从数组的哪个位置开始,参数3:获取的数组的总长度
out.write(b,0,len);
}
//刷写到硬盘
out.flush();
out.close();
in.close();
} catch (Exception e) {
e.printStackTrace();
}
【注】文件字节流非常通用,可以用来操作字符文档,还可以操作任何的其他类型文件(图片、压缩包等),因为字节流直接使用二进制。
5.文件字符流
文件字符流和文件字节流的差别
//建立一个流对象
FileInputStream in =new FileInputStream("text.txt");
//创建一个临时存放数据的数组
byte[] b = new byte[100];
//调用流对象方法将流中的数据读入到数组中
in.read(b)
/**
*文件字符输入流
*/
//建立一个流对象
FileReader fr = new FileReader("Test.txt");
//创建一个临时存放数据的数组
char[] ch = new char[1024];
//调用流对象方法将流中的数据读入到数组中
fr.read(ch);
【注】字符流只适合操作字符文档
6.处理流之一:缓冲流
(1) 缓冲流就是先把数据缓冲内存里,在内存中去做io操作。
(2)缓冲字节输入流
/**
* 缓冲字节输入流
* testBUfferedInputStream
*/
public static void testBUfferedInputStream() throws Exception{
//文件字节输入流对象
FileInputStream in = new FileInputStream("F:/yuanma/src/tt.txt");
//把文件字节输入流放到缓冲字节输入流对象
BufferedInputStream br = new BufferedInputStream(in);
byte[] b = new byte[10];
int len = 0;
while((len = br.read(b)) != -1){
System.out.println(new String(b,0,len));
}
br.close();
in.close();
}
(3)缓冲字节输出流
/**
* 缓冲字节输出流
* BUfferedOutputStream
*/
public static void testBUfferedOutputStream()throws Exception{
//创建字节输出流对象
FileOutputStream out =new FileOutputStream("F:/yuanma/src/tt1.txt");
//把字节输出流方法缓冲字节输出流
BufferedOutputStream bo = new BufferedOutputStream(out);
String s = "hello word!";
//写到内存中
bo.write(s.getBytes());
bo.flush();
bo.close();
out.close();
}
(3)缓冲实现文件的复制
/**
* 缓冲流实现文件的复制
*/
public static void copyFile() throws Exception{
//缓冲输入流
BufferedInputStream br = new BufferedInputStream(new FileInputStream("F:/yuanma/src/tt1.txt"));
//缓冲输出流
BufferedOutputStream bo = new BufferedOutputStream(new FileOutputStream("F:/yuanma/src/tt2.txt"));
byte[] b = new byte[1024];
int len = 0;
while((len = br.read(b)) != -1){
bo.write(b,0,len);
}
bo.close();
br.close();
}
7.处理流之二:转换流
(1)定义:可以把字节流转换为字符流,当字节流中的数据都是字符的时候,用转换流转为字符流处理效率更高。
/**
* 所有文件都是有编码格式
*TXT和java文件一般来讲三种编码
* ISO8859-1,西欧编码,是纯粹英文编码,不适应汉字
* GBK和UTF-8,这两编码是适用于中文和英文
*我们一般使用UTF-8编码
*/
(2)转换字节输入流为字符输入流
/**
* 转换字节输入流为字符输入流
* 注意在转换字符流使,设置的字符集编码要与读取的文件一致
*/
public static void testInputStreamReader() throws Exception{
FileInputStream fs = new FileInputStream("F:/yuanma/src/test/tt5.txt");
//把字节流转换为字符流,参数1:字节流,参数2:是编码
InputStreamReader in = new InputStreamReader(fs,"GBK");
char[] c = new char[100];
int len = 0;
while((len = in.read(c)) != -1){
System.out.println(new String(c,0,len));
}
in.close();
fs.close();
}
(2)转换字节输出流为字符输出流
/**
*转换字节输出流为字符输出流
* @throws Exception
*/
public static void tesstOutputStreamWrite() throws Exception{
FileOutputStream out = new FileOutputStream("F:/yuanma/src/test/tt6.txt");
OutputStreamWriter os = new OutputStreamWriter(out,"UTF-8");
os.write("年号你好");
os.flush();
os.close();
out.close();
}
8.处理流之三:标准输入输出流
(1)标准输入流
/**
* 标准的输入流
* @throws Exception
*/
public static void testSystemIn() throws Exception{
//创建一个接收键盘输入数据的输入流
InputStreamReader is = new InputStreamReader(System.in);
//把输入流放入缓冲流
BufferedReader br = new BufferedReader(is);
String str = "";//定义一个接收数据的字符串
while((str = br.readLine()) != null){
System.out.println(str);
}
br.close();
is.close();
}
(2)案例
/**
* 把控制台输入的内容写到指定的TXT文件中,当接受到的字符串为over时,结束程序的运行
*/
public static void write2TXT() throws Exception{
//创建一个接收键盘输入数据的输入流
InputStreamReader is = new InputStreamReader(System.in);
//把输入流放入缓冲流
BufferedReader br = new BufferedReader(is);
BufferedWriter out = new BufferedWriter(new FileWriter("F:/yuanma/src/test/tt7.txt"));
String line = "";
while ((line = br.readLine()) != null){
if(line.equals("over")){
break;
}
//读取的每一行都写到txt中
out.write(line);
}
out.flush();
out.close();
br.close();
is.close();
}
9.处理流之四:打印流(了解)
10.处理流之五:数据流(了解)
(1)数据流,专门用来做基本数据类型的读写
(2)数据输出流
/**
* 数据输出流
* 用数据输出流写到文件中的基本数据类型的数据,是乱码的,不能直接辨认,需要数据输入流来读取
*/
public static void tsetDataOutputStream() throws Exception{
DataOutputStream out = new DataOutputStream(new FileOutputStream("F:/yuanma/src/test/tt8.txt"));
// out.writeBoolean(true);
// out.writeDouble(1.35d);
out.writeInt(100);
out.flush();
out.close();
}
(3)数据的输入流
/**
* 数据的输入流
* 用数据输入流读取数据输出流写到文件中的数据时,要保证使用和当时写的数据类型一致的类型来读取
* 也就是写的时候时writeInt,读的时候就是readInt
*/
public static void testDataInputStream() throws Exception{
DataInputStream in =new DataInputStream(new FileInputStream("F:/yuanma/src/test/tt8.txt"));
System.out.println(in.readInt());
in.close();
}
11.处理流之六:对象流
(1)用于存储和读取对象的处理流
(2)序列化:用ObjectOutputStream将一个Java对象写入IO流中。
反序列化:用ObjiectInputStream类从IO流中恢复该Java对象。
序列化与反序列化针对的是对象的各种属性,不包括类的属性。
/**
* 序列化与反序列化
* 对象的序列化与反序列化使用的类要严格一致,包名、类名、类机构等等都要一致
*/
public class tset05 {
public static void main(String[] args){
try {
// tset05.testSerialize();
tset05.testDeserialize();
} catch (Exception e) {
e.printStackTrace();
}
}
/**
* 对象的序列化
* @throws Exception
*/
public static void testSerialize()throws Exception{
//定义对象的输出流,把对象序列化之后的流放到指定的文件中
ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream("F:/yuanma/src/test/tt9.txt"));
Person p = new Person();
p.name = "zhangsan";
p.age = 11;
out.writeObject(p);
out.flush();
out.close();
}
/**
* 对象的反序列化
*/
public static void testDeserialize() throws Exception{
//创建对象的输入流,从指定的文件中把对象序列化后的流读取出来
ObjectInputStream in = new ObjectInputStream(new FileInputStream("F:/yuanma/src/test/tt9.txt"));
Object obj = in.readObject();
Person p =(Person)obj;
System.out.println(p.name);
System.out.println(p.age);
in.close();
}
}
12.RandomAccessFile类
【注】如果是在文件开头或者中间的某个位置开始写的话,就会覆盖掉等长度的内容
/**
* 随机读文件
*/
public static void testRandomAccessFileRead()throws Exception{
//参数1:文件路径 参数2:指定的访问模式
//r:以只读的方式打开
//rw:打开以便读取和写入
//rwd:打开以便读取和写入,同步文件内容更新
//rwa:打开以便读取和写入,同步文件内容和元数据的更新
RandomAccessFile ra = new RandomAccessFile("F:/yuanma/src/test/tt10.txt","r");
ra.seek(8);//设置读取文件内容的起始点
byte[] b = new byte[1024];
int len = 0;
while((len = ra.read(b)) != -1){
System.out.println(new String(b,0,len));
}
}
/**
* 随机写
*/
public static void testRandomAccessFileWrite()throws Exception{
RandomAccessFile ra = new RandomAccessFile("F:/yuanma/src/test/tt10.txt","rw");
// ra.seek(6);//设置写的起始点,0代表从头写
ra.seek(ra.length());//表示从末尾写,追加
ra.write("你好".getBytes());
ra.close();
}
}
第十章 反射
10.1概念
1.反射前提
java已经加载过这个类,就可以通过类名来寻找这个类的所有相关信息。
2.反射(Reflection)
动态语言的关键,反射机制允许程序在执行器借助于Reflection API取得任何内部类的内部信息,并能直接操作任意对象的内部属性及方法。
10.2class类
1.常用方法
2.通过反射调用类的完整结构
(1)获取父类和接口
package test01;
public class test1 {
public static void main(String[] args){
try {
//通过包名、类名的字符串,调用Class.forName方法获取指定类的Class实例
Class clazz = Class.forName("test01.Student");
//获取父类
Class superClazz = clazz.getSuperclass();
System.out.println(superClazz.getName());
//获取当前类的所有接口
Class[] interfaces = clazz.getInterfaces();
for(Class c : interfaces){
System.out.println("接口:"+c.getName());
}
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
}
(2)获取类的全部构造器
package test01;
import java.lang.reflect.Constructor;
public class test1 {
public static void main(String[] args){
try {
//获取构造器
//获取所有共有的构造方法
Constructor[] cons = clazz.getConstructors();
for(Constructor c : cons){
System.out.println("构造方法:"+c.getName());
System.out.println("构造方法:"+c.getName()+"的修饰符是:"+c.getModifiers());
Class[] paramClazz = c.getParameterTypes();
for(Class pc : paramClazz){
System.out.println("构造方法"+c.getName()+"的参数类型是:"+pc.getName());
}
}
//获取所有的构造方法,1代表共有,2代表私有
Constructor[] cons1 = clazz.getDeclaredConstructors();
for(Constructor c : cons1){
System.out.println("构造方法:"+c.getName());
System.out.println("构造方法:"+c.getName()+"的修饰符是:"+c.getModifiers());
}
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
}
(3)通过反射创建对象
package test01;
import java.lang.reflect.Constructor;
public class test1 {
public static void main(String[] args){
try {
//通过包名、类名的字符串,调用Class.forName方法获取指定类的Class实例
Class clazz = Class.forName("test01.Student");
try {
//调用Student类的无参共有构造
Object obj = clazz.newInstance();
Student stu = (Student)obj;
//指定获取有一个参数并且为string类型的公有的构造方法
Constructor c = clazz.getConstructor(String.class);
Student stu1 = (Student)c.newInstance("第一中学");
System.out.println(stu1.school);
//同构反射机制,可以强制调用私有的构造方法
//指定获取有两个参数并且为string和int类型的私有的构造方法
Constructor c = clazz.getDeclaredConstructor(String.class,int.class);
//解除私有的封装,下面就可以对这个私有方法强制调用
c.setAccessible(true);
Student stu = (Student)c.newInstance("zhanghsan",12);
} catch (Exception e) {
e.printStackTrace();
}
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
}
}
(4)通过反射调用类中的指定方法、属性
/**
* 注意:下面不论是反射调用setInfo还是test方法
* 都调用的obj对象的方法,obj对象实际上就是student对象
*/
//获取无参构造
Class clazz = Class.forName("test01.Student");
Constructor con = clazz.getConstructor();
//使用无参构造创建对象
Object obj = con.newInstance();
//得到名称setInfo的方法
Method m = clazz.getMethod("setInfo", String.class, String.class);
//参数1是需要实例化的对象,后面的参数是调用当前的方法实际参数
m.invoke(obj,"zhangsan","第一中学");
//调用私有方法
Method m1 = clazz.getDeclaredMethod("test", String.class);
m1.setAccessible(true);
m1.invoke(obj,"lisi");
3.java动态处理
(1)接口
package test02;
public interface ITestDemo {
public void test1();
public void test2();
}
(2)实现接口
package test02;
public class TestDemoImpl implements ITestDemo{
@Override
public void test1() {
System.out.println("执行Test1方法");
}
@Override
public void test2() {
System.out.println("执行Test2方法");
}
}
(3)实现代理类
package test02;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
public class ProxyDemo implements InvocationHandler{
Object obj;//被代理的对象
public ProxyDemo(Object obj){
this.obj = obj;
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
System.out.println(method.getName()+"方法开始执行");
//执行的是指定代理对象的指定方法
Object result = method.invoke(this.obj,args);
System.out.println(method.getName()+"方法执行完毕");
return null;
}
}
(4)测试
package test02;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;
public class test2 {
public static void main(String[] args){
ITestDemo test = new TestDemoImpl();
/**
* 如果一个对象想要通过Proxy.newProxyInstance方法代理
* 那么这个对象的类一定要有相应的接口
* 就像本类中的ITestDemo接口和TestDemoImpl
*/
test.test1();
test.test2();
System.out.println("***************************");
/**
* 需求:
* 在执行test1和test2前打印开始执行
* 执行完后打印执行结束
*/
InvocationHandler handler = new ProxyDemo(test);
/**
* Proxy.newProxyInstance(ClassLoader(),interfaces,h)
* 参数1是代理对象的类加载器
* 参数2是被代理的对象的接口
* 参数3是代理对象
*
* 返回的值成功被代理后的对象
*/
ITestDemo t = (ITestDemo) Proxy.newProxyInstance(handler.getClass().getClassLoader(),test.getClass().getInterfaces(),handler);
t.test1();
t.test2();
}
}
第十一章 线程
11.1基本概念
1.概念
程序:未完成某种任务,用某种语言编写的一组指令的集合。
线程:程序的一次执行过程,或是正在运行的一个程序
进程:进程可进一步细化为线程,是一个程序内部的一条执行路径,若一个程序可同时执行多个线程,就是支持多线程的。
11.2多线程的创建和启动
1.构造方法
通过继承Threa类实现多线程
package test03;
/**
* 通过继承Threa类实现多线程
*/
public class TestThread extends Thread{
@Override
public void run(){
System.out.println("多线程运行的代码");
for(int i = 0;i < 5; i++){
System.out.println("这是多线程的逻辑代码:"+i);
}
}
}
package test03;
public class Test {
public static void main(String[] args){
Thread t0 = new TestThread();
t0.start();//启动线程
System.out.println("*******************");
System.out.println("*******************");
System.out.println("*******************");
/**
* 从控制台多次运行main方法
* 发现main方法中打印的3行与开启线程运行run方法中打印的是混合的
* main方法执行t.start()方法开启多线程之后,就相当于在main方法之外开启一个直流
* 这个时候t0.start之后的main方法的其他代码的运行就与run方法运行无关了
* 就像两条河流,各走各的
* 控制台输出的结果就是两条并行程序的运行结果综合,这个结果要拆开两部分看
* 各自保持自己的输出顺序
*/
}
}
通过实现Runnable接口实现多线程
package test03;
/**
* 通过实现Runnable接口实现多线程
*/
public class TestRunnable implements Runnable {
@Override
public void run() {
System.out.println(Thread.currentThread().getName()+"多线程运行的代码");
for(int i = 0;i < 5; i++){
System.out.println(Thread.currentThread().getName()+"这是Runnable多线程的逻辑代码:"+i);
}
}
}
package test03;
public class Test {
public static void main(String[] args){
// t0.start();//启动线程
// Thread t3 = new Thread(new TestRunnable());
// t3.start();
Thread t4 = new Thread(new TestRunnable(),"t-1");
t4.start();
Thread t5 = new Thread(new TestRunnable(),"t-2");
t5.start();
System.out.println("*******************");
System.out.println("*******************");
System.out.println("*******************");
}
}
2.继承方式和实现方式的联系与区别
【区别】
继承Thread:线程代码放在Thread子类run方法中。重写run方法
实现Runnable:线程代码放在接口的子类的run方法中。实现run方法
一般使用实现run方法
【实现的好处】
(1)避免了单继承的局限性。
(2)多线程可以共同享用一个接口实现类的对象,非常适合多个相同线程来处理同一份资源。
3.多线程的好处
(1)提高引用程序的响应。对图形化界面更有意义,可增强用户体验。
(2)提高计算机系统CPU的利用率。
(3)改善程序结构。将即长又复杂的进程分为多个线程,独立运行,利于理解和修改。
4.Thread的方法
package test03;
public class Test1 {
public static void main(String[] args){
TestRun run0 = new TestRun();
TestRun run1 = new TestRun();
Thread t0 = new Thread(run0);
Thread t1 = new Thread(run1);
//设置优先级
// t0.setPriority(1);
// t1.setPriority(10);
t0.start();
t1.start();
// t0.setName("线程t0");//设置线程的名称
// System.out.println(t0.getName());
// System.out.println(t1.getName());
/**
* 优先级:就是那个线程有较大的概率被执行
* 优先级用数组1-10来表示,数字越大优先级越高,默认为5
*/
//获取优先级
// System.out.println("t0的优先级"+t0.getPriority());
System.out.println("***************1");
System.out.println("***************2");
System.out.println(t1.isAlive());//判断当前线程是否存活
// t1.stop();强制线程生命周期结束,强制停止此线程
try {
t0.join();//相当于在这块把t0的run的代码插入到这个位置执行
/**
* 就是阻塞当前的main方法,先不执行System.out.println("***************3");
* 先执行join进来的线程代码
* join的线程执行完毕之后在继续执行之前main方法阻塞的代码
*/
} catch (InterruptedException e) {
e.printStackTrace();
}
System.out.println("***************3");
}
}
class TestRun implements Runnable{
int count = 0;
@Override
public void run() {
System.out.println(Thread.currentThread().getName() + "多线程运行的代码");
for (int i = 0; i < 5; i++) {
// try {
// Thread.sleep(1000);//当前线程睡眠1000毫秒
// //相当于当前的这个循环每隔1000毫秒执行一次循环
// } catch (InterruptedException e) {
// e.printStackTrace();
// }
// if(i % 2 == 0){
// //线程让步
// Thread.yield();
// }
count++;
System.out.println(Thread.currentThread().getName() + "这是Runnable多线程的逻辑代码:" + count);
}
}
11.4线程的生命周期
1.五种状态
(1)新建:线程实例的创建
(2)就绪:执行.start()方法之后
(3)运行:run的代码开始运行
(4)阻塞:类似堵车,run方法暂停,卡住run方法
(5)死亡:线程完成了它的全部工作,执行.stop()方法或者断电或者杀掉线程被提前强制性的中止

11.5线程的同步
1.synchronized的使用方法
package test03;
public class Test2 {
public static void main(String[] args){
//定义账户对象
Account a = new Account();
//多线程对象
User u_weixin = new User(a,2000);
User u_zhifubao = new User(a,2000);
Thread weixin = new Thread(u_weixin,"微信");
Thread zhifubao = new Thread(u_zhifubao,"支付宝");
weixin.start();
zhifubao.start();
}
}
class Account{
public static int money = 3000;
/**
* 提款,判断账户金额够不够
* 多线程调用,线程共享资源时,一个线程执行这个方法没有完毕时,另一个线程又执行这个方法
* 解决方法:先让一个线程执行完毕,在执行另一个线程
* 使用synchronized同步锁来完成
* 可直接在方法上加上synchronized关键字
* 在普通方法上加synchronized,锁的时整个对象,不是某一个方法
* 不同的对象就是不同的锁,普通方法加synchronized,线程使用不同的此方法的对象,还有共享资源的问题
* @param m
*/
public synchronized void drawing(int m){
String name = Thread.currentThread().getName();
if(money < m){
System.out.println(name+"操作,账户金额不足"+money);
}else {
System.out.println(name+"操作,账户原有金额:"+money);
System.out.println(name+"操作,取款金额:"+m);
money = money - m;
System.out.println(name+"操作,取款后余额:"+money);
}
}
}
class User implements Runnable{
Account account;
int money;
public User(Account account,int money){
this.account = account;
this.money = money;
}
@Override
public void run() {
account.drawing(money);
}
}
2.线程的死锁问题
(1)死锁:不同的线程分别占用对方需要的同步资源不放弃,都在等待对方放弃自己需要的同步资源,就形成了线程的死锁。
(2)解决方法:
专门的算法、原则,比如枷锁顺序一致;
经量减少同步资源的定义,尽量避免锁未释放的场景。
11.6线程通信
1. 三种方法
wait():挂起当前线程;
notify():唤醒正在等待资源优先级最高的线程;
notifyall():唤醒所有正在等待资源的线程。
【注】这三个方法只能用在有同步锁的方法或者代码块中。
2.用法
package test03;
public class Test2 {
public static void main(String[] args){
//定义账户对象
Account a = new Account();
//多线程对象
User u_weixin = new User(a,2000);
User u_zhifubao = new User(a,2000);
Thread weixin = new Thread(u_weixin,"微信");
Thread zhifubao = new Thread(u_zhifubao,"支付宝");
weixin.start();
zhifubao.start();
}
}
class Account{
public static int money = 3000;
public synchronized void drawing(int m,Account a){
String name = Thread.currentThread().getName();
/**
* 如果时微信操作,等待
*/
if(name.equals("微信")){
try {
a.wait();//当前进程进入阻塞状态
} catch (InterruptedException e) {
e.printStackTrace();
}
}
if(money < m){
System.out.println(name+"操作,账户金额不足"+money);
}else {
System.out.println(name+"操作,账户原有金额:"+money);
System.out.println(name+"操作,取款金额:"+m);
money = money - m;
System.out.println(name+"操作,取款后余额:"+money);
}
if(name.equals("支付宝")){
a.notify();//唤醒当前优先级最高的线程,进入就绪状态
}
}
}
class User implements Runnable{
Account account;
int money;
public User(Account account,int money){
this.account = account;
this.money = money;
}
@Override
public void run() {
account.drawing(money,account);
}
}
11.7经典例题:生产者/消费者问题
package test03;
/**
* 生产者/消费者问题
*/
public class Test3 {
public static void main(String[] args){
Clerk c = new Clerk();
//生产时不消费,消费时不生产
//生产者
new Thread(new Runnable() {
@Override
public void run() {
synchronized (c){
while (true){//无限循环代表无限的生产次数
if(c.productNum == 0){//产品数为0开始生产
System.out.println("生产数为0,开始生产");
while (c.productNum < 4){
c.productNum++;//生产产品
System.out.println("库存:"+c.productNum);
}
System.out.println("生产数为"+c.productNum+",结束生产");
c.notify();//唤醒消费者线程
}else {
try {
c.wait();//生产者等待
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
},"生产者").start();
//消费者
new Thread(new Runnable() {
@Override
public void run() {
synchronized (c){
while (true){//无限循环代表无限的消费次数
if(c.productNum == 4){//产品数为4开始消费
System.out.println("生产数为4,开始消费");
while (c.productNum > 0){
c.productNum--;//消费产品
System.out.println("库存:"+c.productNum);
}
System.out.println("消费数为"+c.productNum+"结束消费");
c.notify();//唤醒生产者线程
}else {
try {
c.wait();//消费者等待
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
}
},"消费者").start();
}
}
class Clerk{
public static int productNum = 0;
}
浙公网安备 33010602011771号