程序
程序,即一系列按照特定顺序组织的计算机数据和指令的集合。有系统程序和应用程序之分
1、Java语言的特点
- java语言简单易用
- 跨平台性:java的jvm虚拟机可以让java程序在不同版本的系统上运行,无需考虑兼容性,即一次编译,处处运行。(JVM实现跨平台)
- 面向对象性:让编程更简单
- 支持多线程:可以很好的解决高并发的问题
2、多线程
多线程(multithreading),是指从软件或者硬件上实现多个线程并发执行的技术。具有多线程能力的计算机因有硬件支持而能够在同一时间执行多于一个线程,进而提升整体处理性能。具有这种能力的系统包括对称多处理机、多核心处理器以及芯片级多处理或同时多线程处理器。在一个程序中,这些独立运行的程序片段叫作“线程”(Thread),利用它编程的概念就叫作“多线程处理”。
3、Java语言的跨平台
- 什么是跨平台
通过java开发的应用程序可以再不同的操作系统中去运行
- 怎么实现跨平台
通过java虚拟机实现
4、Java运行机制
java两大核心机制
-
Java虚拟机(JVM):用于运行java应用程序,是java平台的核心
-
垃圾回收机制(GC):自动进行
5、JDK、JRE
-
什么是jdk:一个开发工具包,用于开发程序
-
jdk包含jre
-
什么是jre:包含jvm虚拟机和java程序的Api,java运行环境,编写好的java程序需要通过jre去运行
-
jdk >jre> jvm之间的关系
jdk:开发java程序
jre:运行java程序
jvm:跨平台的核心 -
jdk的介绍:JDK的基本组件包括:.javac —编译器,将源程序转为字节码(把java文件编译成class文件);.jar —打包工具,将相关的类文件打包成一个文件;.javadoc —文档生成器,从源码注释中提取文档;.jdb —debugger,查错工具;.java —运行编译后的Java程序(后缀为.class)
6、Java程序的开发步骤
- 1、编写一个java程序
- 2、通过javac将java文件编译成class文件
- 3、通过类加载器加载class文件
- 4、虚拟机去执行程序
public class HelloWorld{
//程序的入口
public static void mian(String[] args){
System.out.print("HelloWorld");
}
}
7、关键字
- java特殊定义的一些单词,每个关键字都有自己独有的功能
- 全部小写
8、标识符
- 类名、接口名、方法名等
- 以字母、下划线“_”和“ ” 符 开 头 , 后 面 可 跟 字 母 、 下 划 线 “ ” 和 “ ”符开头,后面可跟字母、下划线“_”和“ ”符开头,后面可跟字母、下划线“”和“”符或数字
9、java的基本数据类型
Java中最小的计算单元为字节,1字节=8位(bit)
- 八种:byte:长度为1字节8bit,取值10000000(-128)到 01111111(127),变量初始化默认值为0,包装类Byte
- short:长度为2字节16bit,取值10000000 00000000(-32768)到 01111111 11111111(32767),变量初始化默认值为0,包装类Short
- int:长度为4字节32bit,取值-2^31 (-2,147,483,648)到 2^31-1(2,147,483,647),变量初始化默认值为0,包装类Integer
- long:长度为8字节64bit,取值-2^63 (-9,223,372,036,854,775,808)到 2^63-1(9,223,372,036,854,775,8087),变量初始化默认值为0或0L,包装类Long
- float:属于Java中的浮点型,也叫单精度浮点型,长度为4字节32bit,变量初始化默认值0.0f,包装类Float
- double :属于Java中的浮点型,也叫双精度浮点型,长度为8字节64bit,变量初始化默认值0.0d,包装类Double
- char:属于java中的字符型,占2字节16bit,可以赋值单字符以及整型数值, 变量初始化无默认值,包装类Character
- boolean:仅有两个值true, false,变量初始化默认值false
- 如果定义成long类型必须在后面加上大写的L或l
- float必须加上F或l
- 引用数据类型
类、 接口类型、 数组类型、 枚举类型、 注解类型、 字符串型
12 .基本数据类型和引用数据类型的区别
基本数据类型:存储在栈中。引用数据类型:存储在堆中,栈中存放的是其具体的内容所在内存的地址,通过地址可以找到具体的内容
10、变量的类型转换
- 自动类型转换:也叫隐式类型转换,指的是在转换过程中不需要显式的进行生明,将范围小的转换为大的,系统自动进行
- 强制类型转换:大转小
- 把字符串类型转换成其他数据类型
//强制类型转换
//需要转成什么类型的,就在变量前面加上该类型就可以
int num = 20;
short s = (short)num;
//将字符串转换成int类型,不能有中文
//每个数据类型都有自己的包装类,包装类都封装了对应的转换方法
String str = "123";
int num = Integer.parseInt(str);
//将int转换成字符串类型
//第一种方式
int num = 123;
String str = num + "";
//第二种方式
String str2 = String.vallueOf(num);
11、转义字符
\n表示换行,换到下一行的开头
12、运算符
- 自增运算,自减运算
- ++m、m++的区别:如果是单独使用没有任何区别,混合使用(1)若果是后++,先使用变量的值,然后+1,先用后加。(2)如果是前++,变量的值会立马加1,如果拿着累加的值进行使用,先加后用
int d = 10;
int e = 10;
int f = (d++)+(++d)+(d--)+(--e)+(e++)+(++e);
//10+12+12+9+9+11=63
- 赋值运算符+=:a=a+b;可以简写成a+=b;
13、逻辑运算符
- 逻辑运算符&和|可以双写
- 双写与单写的区别:短路与:如果左侧已经可以判断最终结果,那么后面的判断将不再执行,开发效率高。
int num = 10;
System.out.println(3>4 & ++num>5);//num = 11
int num1= 10;
System.out.println(3>4 && ++num1>5);//num = 10
14、三元运算符
- 条件表达式?表达式1:表达式2
- 如果条件表达式为true,执行表达式1
- 如果条件表达式为false,执行表达式2
int a = 100;
int b = 200;
String result = (a<b)?"a小":"b小";
15、键盘输入数据
import java.util.Scanner;
public class Demo1 {
public static void main(String[] args) {
// TODO Auto-generated method stub
//Scanner用于接收键盘的数据
Scanner sc = new Scanner(System.in);
String name = sc.next();//接收字符串
int num = sc.nextInt();
double score = sc.nextDouble();
}
}
16、分支语句
Java分支语句有:if语句、switch语句
- 多重if语句
//对学员的结业考试成绩评测成绩>=80 :良好成绩>=60 :中等成绩<60 :差
Scanner sc = new Scanner(System.in);
System.out.println("请输入成绩:");
int score = sc.nextInt();
if(score>=80){
System.out.println("良好");
}else if(score>=60){
System.out.println("中等");
}else{
System.out.println("差");
}
- switch语法
switch(表达式){
case 常量1:
代码块;
break;
case 常量2:
代码块;
break;
default:
代码块3;
break;
}
//switch语句张三参加计算机编程大赛如果获得第一名,将参加山东大学组织的1个月夏令营活动
//如果获得第二名,将奖励惠普笔记本电脑一台如果获得第三名,将奖励2T移动硬盘一个否则,不给任何奖励
System.out.println("请输入排名:");
int ranking = sc.nextInt();
switch(ranking){
case 1:
System.out.println("一个月夏令营活动");
break;
case 2:
System.out.println("奖励笔记本一台");
break;
case 3:
System.out.println("移动硬盘一个");
break;
default:
System.out.println("没有任何奖励");
break;
}
3.if常用于区间判断,switch常用于等值判断。
17、while和do while循环语句
- while语法
初始化;
while(循环条件){
循环体;
累加器;
}
//2019年培养学员25万人,每年增长25%。请问按此增长速度,到哪一年培训学员人数将达到100万人?
int year = 2019;
double students = 250000 ;
while(students<1000000){
students = (students*(1+0.25));
year++;
}
System.out.println("年份:"+year)
//编写一个程序,从键盘读取个数不确定的整数,并且判断读入的正数和负数的个数,输入0时结束程序,
Scanner sc = new Scanner(System.in);
int count1 = 0;
int count2 = 0;
while(true){
System.out.println("请输入整数:");
int num = sc.nextInt();
if(num>0){
count1++;
}else if(num<0){
count2++;
}else{
break;
}
}
System.out.println("负数个数为:"+count2);
System.out.println("整数个数为:"+count1);
- do while语法
do{
循环体;
累加器;
}while(循环条件);
//不断要求用户输入一个数字(输入的都是正整数),当用户输入end的时候,结束操作,同时计算出刚才 输入所有数据的最大值
int max = 0;
boolean flag = true;
do{
System.out.println("请输入整数:");
String num = sc.next();
if(num.equals("end")){
flag = false;
}else{
int num1 = Integer.parseInt(num);
if(num1>max){
max = num1;
}
}
}while(flag);
System.out.println("最大数为:"+max);
18、for循环结构
- for循环语法格式:
for(初始表达式;条件表达式;迭代表达式){
循环语句;
}
- while和for循环:
while:适用在循环次数不固定的场合。for:循环更适合循环次数固定分场合;
//找出100-999之间的水仙花
/*Cba=c*C*c+b*b*b+a*a*a
153=1+125+27
1^3 + 5^3+ 3^3 = 153*/
for(int i=100;i<=999;i++){
//百位
int a = i/100;
//十位
int b = i/10%10;
//个位
int c = i%10;
if(a*a*a+b*b*b+c*c*c==i){
System.out.println(i);
}
}
//某次程序大赛,3个班级各4名学员参赛,计算每个班参赛学员的平均分
for(int i=1;i<=3;i++){
System.out.printf("请输入第%s个班级的学员信息:\n",i);
int sum = 0;
int avg = 0;
for(int j=1;j<=4;j++){
System.out.printf("请输入第%s个学员的成绩:",j);
int score = sc.nextInt();
sum+=score;
}
avg = sum/4;
System.out.printf("第%s个班级的平均成绩为:"+avg,i);
}
- break:一旦执行,整个循环立马结束,多重嵌套循环时只跳出当前循环。
- continue:跳过当前循环,继续执行下一次循环
19、Java数组
- 什么是数组
数组和变量差不多都是存放数据的,不同的是变量只能保存一条数据,数组可以保存多条数据,前提是这些数据必须是同一类型的;
数组是引用数据类型,不是基本数据类型
- 数组的初始化分为两种情况:
(1)动态初始化:指定数组的长度,一旦长度指定,就是数组只能存放对少数组
第一种格式:
int[] array = new int[9];
第二种格式:
double[] array1;
array1 = new double[10];
//赋值
array[0] = 10;
array1[10] = 11;
(2) 静态初始化:不指定数组的长度,直接在初始化的同时给数组赋值
第一种格式;
int[] array = new int[]{1,5,6};
第二种格式;
int[] array1;
array1 = new int[]{1,5,6};
最简单的创建方式:
int[] array = {1,5,6};
- 遍历数组
int[] array = new int[]{20,22,25,26,12};
for(int i=0;i<array.length;i++){
System.out.println(array[i]);
}
for(int a:array){
System.out.println(a);
}
- 数组的分类
(1)基本数据类型的数组
byte short int long float double char boolean
0 0 0 0 0.0 0.0 空格 false
(2) 引用数据类型的数组
String 、接口 、自己定义的类型
引用数据类型的默认值为null
20、java中内存的分配
-
java中的内存是怎么进行分配的?
内存的申请和释放都是jvm进行管理的,java程序要运行,jvm会自动的向电脑申请一块内存、
-
把这块内存分为五部分
(1)栈(stack):主要存放局部变量
(2)堆(Heap):凡是new出来的东西都在堆里面,堆当中的数据都有默认原则:
整数 0 、小数0.0 、 布尔 false 、 char 空格、引用数据类型:null
(3)方法区(Method Area):存放的是与.class相关的信息
(4)本地方法区(Native Method Area):与操作系统有关
(5)寄存器(Register):与cpu有关
解析:先在栈里面开辟一个空间保存num变量,然后再对堆里面开辟出来一块空间存放new int[5],长度为5,被分为5块,每一块初始化为默认值0,每一部分生成对应发的下标,并在此基础上生成一个地址值,加入为0x111。并将堆中的地址值赋值给栈中的num,然后再给相应的元素赋值。通过num变量的地址值找到堆中对应的空间进行赋值
- 思考
int[] a;
a=new int[3];
a=new int[4];
可以对a重新赋值
//1.求出学生成绩的总分、平均分、最高分、最低分
int[] array = {88,78,76,98,79};
int sum = 0;
int max = array[0];
int min = array[0];
for(int i=0;i<array.length;i++){
sum+=array[i];
if(max<array[i]){
max = array[i];
}
if(min>array[i]){
min = array[i];
}
}
System.out.println("总分为:"+sum);
System.out.println("平均分为:"+sum/array.length);
System.out.println("最高为:"+max);
System.out.println("最低为:"+min);
//2.{“C语言“,”C++“,”C#“,”Java“,”Python“}; 逆序输出
String[] array = {"C语言","C++","C#","Java","Python"};
for(int i=0;i<array.length/2;i++){
String temp = array[i];
array[i] = array[array.length-1-i];
array[array.length-1-i] = temp;
}
for(String a:array){
System.out.println(a);
}
21、冒泡排序
假设有n个数,则需要比较n-1趟,每一趟所需比较的次数为n减去该趟数,例如第一趟为n-1次
//冒泡排序(升序)
int[] array = {21,3,16,5,20,13};
//外层循环,趟数
for(int i=0;i<array.length-1;i++){
//内层循环,每趟的次数
for(int j=0;j<array.length-1-i;j++){
if(array[j]>array[j+1]){
int temp = array[j];
array[j] = array[j+1];
array[j+1] = temp;
}
}
}
for(int a:array){
System.out.println(a);
}
22、二维数组
- 声明二维数组:
数据类型[][] 数据名;
第一个中括号:二维数组中有几个一位数组 行
第二个中括号:每个一位数组有几个元素 列
- 动态初始化
//表示一个5行7列的数组
int[][] array = new int[5][7]; - 静态初始化
//每一个中括号表示一个数组
int[][] array = new int[][]{{1,2},{1,2,3},{1,2,3,4,5,}}; - 简写
int[][] array = {{1,2},{1,2,3},{1,2,3,4,5,}};
- 遍历二维数组
//遍历二维数组
int[][] array = {{1,2},{1,2,3},{1,2,3,4,5,}};
for(int i=0;i<array.length;i++){
for(int j=0;j<array[i].length;j++){
System.out.print(array[i][j]);
}
System.out.println();
}
//foreach循环遍历
for(int[] i:array){
for(int a:i){
System.out.print(a);
}
System.out.println();
}
- 格式;
int[][] array = new int[3][];//可以
int[][] array = new int[][3]//非法
23、方法
- 什么是方法
方法就是把一堆需要重复执行的业务逻辑代码封装起来,如果程序需要使用这段代码的时候,我们直接调用方法即可,不需要去写重复的代码了。
- 好处:提高代码的重用性、维护性、可读性好、执行效率
- 参数:进入方法的数据。 返回值:方法根据参数处理后的结果。
- 访问修饰符:
public:公共的,在当前项目下的任何地方都可以访问
private:私有的,只能在当前类下访问
protected:受保护的,在同一个包或者其他派生类,子类中可以访问。
default:缺省,只能在当前包下进行访问。
- 方法的返回值类型
- 形参列表:由多个形参组成的列表
形参:方法声明时,小括号里面的值,用于接收调用者实际传过来的数据
实参:方法调用时,实际传入参数的值
7. return
(1)把方法最终处理结果返回给调用者 (2)结束当前方法
- 参数的传递,有两种类型
(1)基本数据类型(以及String):形参的操作不会影响实际的参数
(2)引用数据类型:形参的操作会影响实际参数
解析:先将两个方法加载到方法区,运行程序,先执行main方法,main方法进栈,栈里边开辟一个空间,两个局部变量a和b进栈,下一步调用change方法,取方法区寻找change方法,change方法进栈,也开辟一片空间,并给a和b的值赋值,执行方法体,a和b的值改变,change方法执行完毕之后,释放内存(垃圾回收机制),main方法里边的a和b并没有改变,所以a和b的值仍为10和20。
.解析:先将两个方法加载到方法区,运行程序,先执行main方法,main方法进栈,栈里边开辟一个空间,arr变量进栈,new出来的数组进堆,并分配一个空间,生成一个地址值(0x111),然后将地址值赋给arr变量,下一步调用change方法,取方法区寻找change方法,change方法进栈,也开辟一片空间,将main方法中的arr传递给change中的形参,在change中开辟一个空间保存arr,值也是0x111,然后根据地址值,找到堆中的空间,然后对数组的值进行改变,change方法执行完毕之后,释放内存(垃圾回收机制),堆里面的值已经改变,所以a和b的值为100和200.
24、方法的重载
- 什么是方法的重载:方法名必须相同参数列表可以不同。
(1)参数列表的个数不同。
(2)参数类型不同。
(3)参数类型的顺序不同。
public static int sum(int a,int b){
return a+b;
}
public static int sum(int a,int b,int c){//是重载
return a+b+c;
}
public static int sum(int c,int d){//不是重载,因为和参数名无关
return a+b;
}
public static double sum(int c,int d){//不是重载,因为和方法的返回值类型无关
return a+b;
}
public static double sum(int a,double b){//是重载
return a+b;
}
public static double sum(double a,int b){//是重载
return a+b;
}
public static double sum(int b,double a){//不是重载
return a+b;
}
25、类和对象
- 什么是面向过程:c语言。看重的是过程的开发。在开发程序的时候,看重的是中间的过程,每一个过程都要自己去做。
- 什么是面向对象:java。看重的是类的设计。不看重具体的过程,看重谁能帮我完成这件事,找对象帮我去做。(前期设计类的时候比较复杂,但是后期的复用性扩展性比较好)
- 面向过程和面向对象的优缺点:
(1)面向过程:性能比面向对象高,因为面向对象在实例化时,开销比较大
缺点:维护性、扩展性没有面向对象好。
(2)面向对象:维护箱、扩展性好
缺点:性能低,开销大。
- 面向对象的三大特征:封装、继承、多态
- 类和对象:类是对象的抽象,对象是类的实例。类就像是图纸,而对象就是根据图纸做出来的东西。
- 类包括:成员变量(属性)、成员方法(行为)
- 创建一个对象: 类名 对象名=new 类名();
- 调用属性和行为
对象名.属性名(如果属性没有赋值,将会有一个默认值,因为他在堆里面)
对象名.方法名;
- 内存运行解析
先将两个class文件加载到方法区,然后执行main方法,main方法进栈,并开辟一片空间,在main方法里边保存局部变量car,并在堆里边开辟一片空间保存new car(),并将成员变量加载到里面,并附上默认值,堆里面的成员方法加载方法区里边成员方法的地址值,例如为0x999。同理堆里面的区域生成地址值0x111,给到栈里面的变量car。下一步通过地址值0x111,找到堆里面的空间,找到相应的成员变量,并对其进行改值 。下一步调用成员方法,再根据0x111找到堆里面的空间,找到成员方法,再根据方法的地址值0x999,在方法区里面找到成员方法,然后成员方法会进栈。方法执行完会出栈。对象car1同理。car2局部变量等于car1,car1的地址值为0x222,堆里面已经创建完一个0x222的空间了,不需要再创建了。所有car2的值和car1的值一样。
- 成员变量和局部变量的区别
(1)定义的位置不同:
成员变量定义在类内部方法之外。
局部变量定义在方法内部。
(2)作用域不同
成员变量作用类的内部
局部变量作用在方法之内
(3)默认值不同
成员变量有默认值
局部变量没有默认值
(4)内存的位置不同
成员变量保存在堆里面
局部变量保存在栈里面
(5)成员变量有四种修饰符,局部变量没有,默认与方法的修饰符相同
26、封装
- 当创建完对象以后,如果直接通过“对象名.属性名”的方式给属性赋值的话,会出现一些不符合实际情况的问题,所以我们不考虑使用“对象名.属性名”的方式赋值,而是通过“对象名.方法名()”的方式进行赋值,因为我们可以在方法里面加上大量的业务逻辑,对属性值进行限制。
- 在属性值前面加上private修饰符,就无法使用“对象名.属性名”。例如:private int age;
- 解决方式:
(1)将属性私有化,private
(2)提供公用的 (get and set)方法实现属性的取值和赋值。
- //创建一个学生对象
Student stu = new Student();//new的就是一个构造方法。 - 我们创建对象的时候,会用到一个特殊的方法叫做构造方法,构造器。
作用:(1)创建对象。(2)在创建对象的同时,我们可以调用有参的构造方法直接对属性赋值
27、this
- this代表当前使用的对象或者是正在创建的创建的对象,通过那个对象调用了方法就是this
- this可以修饰成员变量,方法,构造方法 直接在成员变量名或者成员方法名前面加上this
- 可以在构造方法中通过“this(形参)”的方式调用构造方法。(在构造方法内部this(形参)必须放在首行)
public class Person {
private String name;
private int age;
public String getName(){
return name;
}
public void setName(String name){
this.name = name;
}
public int getAge(){
return age;
}
public void setAge(int age){
this.age = age;
}
public Person() {
super();
}
public Person(String name) {
super();
this.name = name;
}
public void sayHello(String name){
System.out.println(this.name+"给"+name+"说你好!");
}
}
public class TestPerson {
public static void main(String[] args) {
// TODO Auto-generated method stub
Person pe = new Person("好大哥");
System.out.println("这是"+pe.getName());
pe.sayHello("小老弟");
}
}
28、继承
- 什么是继承:子类继承父类,允许子类拥有父类的属性和行为
- 优缺点:
优点:提高了代码的复用性
缺点:由于继承会有多层的继承关系,会提高他们之间的依赖关系,耦合度会变高 - 子类除了通过继承获取父类的属性和方法外,还可以单独定义自己独有的属性和方法
public class Person {
String name;
int age;
public String getName(){
return name;
}
public void setName(String name){
this.name = name;
}
public int getAge(){
return age;
}
public void setAge(int age){
this.age = age;
}
public Person() {
super();
}
public Person(String name) {
super();
this.name = name;
}
public void sayHello(String name){
System.out.println(this.name+"给"+name+"说你好!");
}
public void eat(){
System.out.println("正在吃饭");
}
}
public class Student extends Person {
String sNo;
public String getsNo() {
return sNo;
}
public void setsNo(String sNo) {
this.sNo = sNo;
}
public Student(String sNo) {
super();
this.sNo = sNo;
}
public Student() {
super();
}
}
public class TestStudent {
public static void main(String[] args) {
// TODO Auto-generated method stub
Person pe = new Person();
pe.setName("好大哥");
System.out.println(pe.getName());
pe.eat();
Student stu = new Student();
stu.setName("小老弟");
System.out.println(stu.getName());
stu.eat();
}
}
-
方法重写:如果父类的方法满足不了子类的要求时,子类可以对父类的方法进行重写。
(1)要求子类重写的方法名、参数列表、返回值类型必须和父类完全一致
(2)子类重写方法的访问修饰符权限不能低于父亲方法的修饰符权限
(3)子类方法和父类方法必须同为static或非static -
重载(overload)和重写(override)的区别:
(1)方法的重载必须在同一个类中,方法的重写必须存在继承关系
(2)方法重载要求方法名相同,参数列表必须不相同,和返回值无关。
(3)方法重写要求方法名,参数列表,返回值必须和父类一致
- super关键字可以修饰属性、方法、构造方法
(1)当前子类和父类方法的属性出现重名时,默认会调用子类的属性,可以通过“super.属性名”的方式显示的调用父类声明同名属性
(2)子类重写父类方法后,如果子类想再次调用父类被重写的方法 ,可以通过“super.方法名()”的方式调用父类方法
(3)super可以修饰构造方法
注意:super(形参)必须声明在首行,this(形参)和super(形参)只能出现一个
public class Circle {
double radius;
public double getRadius() {
return radius;
}
public void setRadius(double radius) {
this.radius = radius;
}
public Circle() {
this.radius = 1;
}
public double findArea(){
return radius*radius*3.14;
}
}
public class Cylinder extends Circle{
double length;
public double getLength() {
return length;
}
public void setLength(double length) {
this.length = length;
}
public Cylinder() {
this.length=1;
}
public double findArea(){
return super.findArea()*2+this.radius*2*3.14*length;
}
public double findVolume(){
return super.findArea()*length;
}
}
public class TestCylinder {
public static void main(String[] args) {
// TODO Auto-generated method stub
Cylinder cy = new Cylinder();
cy.radius = 2;
cy.length = 2;
System.out.println(cy.findArea());
System.out.println(cy.findVolume());
}
}
- 子类对象的存储过程:
29、多态
- 什么是多态:
(1)同一个动作与不同的对象产生不同的行为
(2)多态指的是一个对象的多种形态
- 多态的体现方式
(1)使用继承:不同的子类重写父类的方法后,体现出来的形式不一样
(2)接口的实现
3.形成多态的必要条件:
(1)继承:存在子类和父类的关系
(2)接口的实现:定义一个类实现对应的接口
(3)重写:子类重写了父类的方法
(4)重载:使用相同的方法名,实现的功能不一样
(5)子类对象的多态性:父类的引用指向子类的实例
- 程序分为两种状态,一个是编译状态,一个是运行状态
对于多态来说,编译时看左边,对象定义的是什么类型就是什么类型,将pet对象看作是Pet类型
运行时看右边,真正执行的对象,也就是子类对象Dog,执行的也是子类重写后的方法。
举例:父类Pet 子类Dog
//子类对象的多态性,父类的引用指向子类的实例
Pet pet = new Dog();
pet.info();
//假设子类有sleep方法,父类没有,父类则不可以调用子类的独有方法。
//在编译期间,程序会把pet对象看作是父类对象,而父类没有sleep方法
pet.sleep();//不可以
- 上转型和下转型、
(1)上转型:将子类对象交给父类引用,可以自动转换
(2)下转型:把父类转成子类 强制类型转换
Dog dog1 = (Dog)pet;
6.对象的多态性(一个对象有多种形态)
(1)父类的引用指向子类的实例
(2)在编译期间调用父类的方法,真正执行的时候,执行的是子类的方法
(4)编译看右边,运行看左边
7.instaceof:判断对象名是否属于该对象的实例:pet instanceof Dog
private String name;
protected int health = 100;
protected int love = 0;
public int getLove() {
return love;
}
public void setLove(int love) {
this.love = love;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getHealth() {
return health;
}
public void setHealth(int health) {
this.health = health;
}
public Pet(String name, int health) {
super();
this.name = name;
this.health = health;
}
public Pet() {
super();
}
public void catchingFlyDisc(){
System.out.println("狗狗再接飞盘");
super.love = super.love+3;
super.health = super.health-10;
System.out.println("亲密度:"+super.love+"健康值:"+super.health);
}
public void swimming(){
System.out.println("企鹅正在洗澡");
super.love = super.love+2;
super.health = super.health-5;
System.out.println("亲密度:"+super.love+"健康值:"+super.health);
}
public void play(Pet pet){
if(pet instanceof Dog){//判断传过来的对象属于哪个实例
Dog dog = (Dog)pet;
dog.catchingFlyDisc();
}else if(pet instanceof Penguin){
Penguin pe = (Penguin)pet;
pe.swimming();
}
}
Pet pet1 = new Dog();
Pet pet2 = new Penguin();
Master ma = new Master();
ma.play(pet1);
ma.play(pet2);
30、final关键字
- final关键字:代表最终的意思
- 可以修饰:
(1)修饰类
(2)修饰方法
(3)修饰成员变量
(4)修饰局部变量 - 修饰的类不能被继承,但是可以继承别的类。
- 修饰的方法不能被重写
- 修饰成员变量,成员变量的值不可改变,成员变量不会再有默认值,并且必须直接对成员变量赋值
- 修饰局部变量,局部变量的值不能改变,一般用他修饰一个常量
31、static关键字
- static修饰成员变量(类本身)
(1)修饰成员变量,这个变量就不属于对象,而属于类本身,我们就可以通过“类名.属性名”
(2)只要通过我这个类创建的对象,这些对象都可以共享这个属性
(3)当其中一个对象对类变量进行更改以后,其他的这个类变量也会更改
(4)类变量是随着类的加载而加载,实例变量属于对象,创建对象是才会加载
(5)类变量放在方法区的静态域里面
(6)类变量的生命周期大于实例变量
将变量nation设置成static,就会在方法区中开辟一个静态区,静态区保存nation变量,s1、s2进栈,之后再堆中分别保存new出来的对象,并生成地址值,分别给到s1、s2,并对name,age改值。然后对静态方法区的nation进行改值,然后堆中的nation指向它。
- static修饰方法(类方法)
(1)修饰方法,这个方法就属于类本身了,我可以通过“类名.方法名()”进行调用
(2)随着类加载
(3)在静态方法里面只能调用静态变量和静态方法,相反在普通方法里面可以调用静态方法和静态变量,因为静态方法或静态变量的加载时机早于实例变量和实例方法的记载时机
(4)不能使用this、super关键字 - static修饰代码块 代码块的作用完成初始化
(1)非静态代码块:
可以给类的属性进行初始化操作,同时还可以调用类的方法(静态的、非静态的),还可以有输出语句,每创建完一个对象,非静态代码块就加载一次
{
name=“张三”;
show();
System.out.println(“非静态代码块”);
}
(2)静态代码块
里面可以有输出语句,随着类加载而加载,只会执行一次,静态代码的加载时机早于非静态代码块,静态代码块里面只能调用类变量和方法
作用:jdbc
static{
…;
}
32、抽象类
- 什么是抽象类
如果一个类表达的是一个抽象的概念,仅仅作为其他派生类的一个父类,主要功能的定义,具体的实现交给他的子类去做
2. 抽象类的特点:
(1)抽象方法所在的类必须是抽象类
(2)抽象类不能进行实例化
(3)如果要实现一个抽象类,创建一个子类去继承这个抽象类
(4)子类继承抽象类以后,必须重写抽象类里面所有的抽象方法(前提是子类不是抽象类)
(5)抽象类里面可以定义普通方法
(6)抽象类可以定义构造方法
public abstract class Animal{
public abstract void eat();
}
public class Dog extends Animal{
public void eat(){
System.out.println("狗爱吃骨头")
}
}
public static void main(String[] args){
Animal dog = new Dog();
doo.eat();
}
- 抽象方法的特点:只有方法的定义,没有方法体
public abstract class Animal{
public abstract void eat();
}
33、接口
- 什么是接口?
(1)就是多个类之间的公共规范
(2)接口就是一个特殊的抽象类型,接口里面都是抽象方法
(3)接口解决了java单继承的问题。 - 定义接口:
public interface 接口名{
…
}
- 接口的特点:
(1)接口里面的属性都是常量,默认会加上“public static final修饰
(2)接口里面的方法都是抽象方法,默认会加上“public abstract”进行修饰。
(3)接口不能实例化
(4)抽象类是继承,接口是用来实现的
(5)一个类实现一个接口,必须实现这个接口里面所有的抽象方法(前提这个类不是抽象类)
(6)一个类可以实现多个接口,中间用逗号隔开
(7)接口里面不能定义普通方法
(8)接口没有构造方法
(9)接口也可以继承接口
4.实现类
(1)实现接口的类叫做实现类
(2)如果一个类继承一个父类同时需要实现一个接口,继承一定是在实现接口前面
class A extends B implements C
5.抽象类和接口的相同点:
(1)接口和抽象类都不能被实例化。只能被其他类实现和继承
(2)接口和抽象类都可以包含抽象方法,实现接口和抽象类的都必须实现这些抽象方式,否则实现的类就是抽象类。
6 不同点:
(1)抽象类与接口定义不同:abstract interface
(2)接口里只能包含抽象方法,不包含已经实现的方法;抽象类则完全可以包含普通方法
(3)接口里不能定义静态方法;抽象类可以定义静态方法
(4)接口里只能定义静态常量属性,不能定义普通属性;抽象类里既可以定义普通属性,也可以定义静态常量
(5)接口不包含构造函数;抽象类可以包含构造函数,抽象类里的构造函数并不是用于创建对象,而是让其他子类调用这些构造函数来完成属于抽象类的初始化操作。
(6)接口不包含初始化块,抽象类可以包含初始化块
(7)一个类最多只能有一个直接父类,包括抽象类;但一个类可以直接实现多个接口,通过实现多个接口可以弥补java的单继承
举例1:比较两个圆的半径大小
public class Circle {
private Double radius;
public Double getRadius() {
return radius;
}
public void setRadius(Double radius) {
this.radius = radius;
}
public Circle(Double radius) {
super();
this.radius = radius;
}
public Circle() {
super();
}
}
public interface CompareObject {
public int compareTo(Object o);
}
public class ComparableCircle extends Circle implements CompareObject {
public ComparableCircle(double r) {
// TODO Auto-generated constructor stub
super(r);
}
@Override
public int compareTo(Object o) {
// TODO Auto-generated method stub
ComparableCircle c2 = (ComparableCircle)o;
if(this.getRadius()>c2.getRadius()){
System.out.println(1);
}else if(this.getRadius()==c2.getRadius()){
System.out.println(0);
}else{
System.out.println(-1);
}
return 0;
}
}
public class TestInterface {
public static void main(String[] args) {
// TODO Auto-generated method stub
ComparableCircle c1 = new ComparableCircle(2.0);
ComparableCircle c2 = new ComparableCircle(3);
c1.compareTo(c2);
}
}
举例2:电脑、鼠标、键盘的使用
public interface Usb {
void open();
void close();
}
public class Mouse implements Usb{
@Override
public void open() {
// TODO Auto-generated method stub
System.out.println("打开鼠标");
}
@Override
public void close() {
// TODO Auto-generated method stub
System.out.println("关闭鼠标");
}
public void click(){
System.out.println("点击一下");
}
public void click2(){
System.out.println("点击两下");
}
}
public class KeyBoard implements Usb {
@Override
public void open() {
// TODO Auto-generated method stub
System.out.println("打开键盘");
}
@Override
public void close() {
// TODO Auto-generated method stub
System.out.println("关闭键盘");
}
public void input(){
System.out.println("键盘输入");
}
}
public class Computer {
public void poweropen(){
System.out.println("电脑开机");
}
public void poweroff(){
System.out.println("电脑关机");
}
public void use(Usb usb){
usb.open();
if(usb instanceof Mouse){
Mouse m = (Mouse)usb;
m.click();
m.click2();
}else if(usb instanceof KeyBoard){
KeyBoard k = (KeyBoard)usb;
k.input();
}
usb.close();
}
}
public class TestComputer {
public static void main(String[] args) {
// TODO Auto-generated method stub
Computer com = new Computer();
com.poweropen();
Usb m = new Mouse();
Usb k = new KeyBoard();
com.use(m);
//com.use(k);
com.poweroff();
}
}
34、Objec类
- Object类是所有Java类的根父类
- 如果在类的声明中未使用extends关键字指明其父类,则默认父类为java.lang.Object类
Order order = new Order();
order.getClass.getSuperclass();//找到创建类的父类
3.Object类中的功能(属性、方法)就具有通用性
属性:无
方法:equals() toString() //重点
4.Object类只声明了一个空参的构造器
5.==:运算符
(1)可以使用在基本数据类型变量和引用数据类型变量中
(2)如果比较的是基本数据类型变量:比较两个变量保存的数据是否相等。(不一定类型要相同)
int i = 10; char c = 10; i= =c // true
(3)如果比较的是引用数据类型变量:比较两个对象的地址值是否相同。
6 .equals()方法的使用:
(1)是一个方法,而非运算符
(2)只能适用引用数据类型
(3)Object类中equals()的定义:
public boolean equals(Object obj) {
return (this == obj);
}
说明:Object类中equals()和==的作用是相同的,比较两个对象的地址值是否相同
(4)像String、Date、File、包装类都重写了Object类中的equals方法。重写以后,比较的不是两个引用的地址是否相同,而是比较两个对象的“实体内容”是否相同。
(5)通常情况下,我们自定义的类如果使用equals()的话,也通常是比较两个对象的“实体内容”是否相同,那么就需要我们对Object类中的equals()进行重写。
重写原则,比较两个对象的实体内容是否相同。
@Override
public boolean equals(Object obj) {
if(this == obj){
return true;
}
if(obj instanceof Customer){
Customer c = (Customer)obj;
return this.getAge()==c.getAge() && this.getName().equals(c.getName());
}
return false;
}
public static void main(String[] args) {
// TODO Auto-generated method stub
Customer c1 = new Customer(1,"a");
Customer c2 = new Customer(2,"a");
System.out.println(c1.equals(c2));
}
7.Object类 中toString的使用:
(1)当我们输出一个对象的引用时,实际上就是调用当前对象的toString();
(2)Object类中toString的定义:
public String toString() {
return getClass().getName() + "@" + Integer.toHexString(hashCode());
}
(3像String、Date、File等都重写了Object类中的toString()方法.使得在调用对象的toString()时返回“实体内容”信息
35、String类
- String:声明为final的,不可被继承
- String实现了Serializable接口:表示字符串是支持序列化的。实现了Comparable接口:表示String可以比较大小。
- String内部定义了final char[] value 用于存储字符串数据
- String:代表不可变的字符序列。简称不可变性。
体现:(1)当对字符串重新赋值时,需要重新指定内存区域赋值,不能使用原有的value进行赋值。
(2)当对现有的字符串进行连接操作时,也需要重新指定内存区域赋值,不能使用原有的value进行赋值
(3)当调用String的replace()方法修改指定的字符时,也需要重新指定区域赋值,,不能使用原有的value进行赋值 - 通过字面量的方式(区别于new的方式)给一个字符串赋值,此时的字符串值声明在字符串常量池中。
- 字符串常量池中是不会存储相同内容的字符串
String s1 = "abc"//字面量的定义方式
String s2 ="abc";
system.out.println(s1 == s2);//比较地址值 true
- String的实例化方式:
方式一:通过字面量的定义方式:生成的数据声明在方法区中。
方式二:通过new + 构造器的方式:此时的数据,是在堆中开辟空间。
String s2 = new String ("Tom");
String s3 = new String ("Tom");
String s1 = "Tom";
s1 == s3//false
s2 == s3//false
Person p2 = new Person ("Tom");
Person p3 = new Person ("Tom");
p2.name == p3.name;//true
面试题:String a = new String(“abc”);方式创建对象,在内存中创建了两个对象,一个是堆空间中的new结构 ,另一个是char[] 对应的常量池中的数据:“abc”;
2.常量与常量的拼接结果在常量池。且常量池中不会存在相同的内容常量。只要其中有一个变量,结果就在堆中
String a1 = "helllo";
String a2 = "word";
String a3 = "helloword";
String a4 = "hello" + "word";
Strig a5 = a1 + "word";
a3 == a4; true
a3 == a5;false
3.String常用的方法
String str1 = "HelloWorld";
//返回字符串长度
System.out.println(str1.length());
//返回某索引处的字符
System.out.println(str1.charAt(0));
//判断是否是空字符串
System.out.println(str1.isEmpty());
//将String所有字符转换为小写
System.out.println(str1.toLowerCase());
//将String所有字符转换为大写
System.out.println(str1.toUpperCase());
String str2 = " Hello World ";
//返回字符串副本,忽略前部空白和尾部空白
System.out.println(str2.trim());
String str3 = "abc";
//将指定的字符串连接到此字符串的结尾,等价于“+”
System.out.println(str1.concat(str3));
//计较两个字符串的大小
System.out.println(str1.compareTo(str3));
//此字符串的从beginIndex开始截取到最后一个子字符串
System.out.println(str1.substring(3));
//此字符串的从beginIndex开始截取到endIndex(不包含)的一个子字符串
System.out.println(str1.substring(0, 5));
String str1 = "helloworld";
//测试此字符串是否以指定的后缀结束
System.out.println(str1.endsWith("ld"));
//测试此字符串是否以指定的前缀开始
System.out.println(str1.startsWith("he"));
//测试此字符串是否从指定的索引开始的子字符串是否相同
System.out.println(str1.startsWith("ll",2));
//当且仅当此字符串包含指定的char值序列时,返回true
System.out.println(str1.contains("wo"));
//返回指定字符串在此字符串中第一次出现处的索引
System.out.println(str1.indexOf("ll"));
//从指定索引位置开始找
System.out.println(str1.indexOf("lo", 5));
//返回指定子字符串在此字符串中最右边出现处的索引(索引认识从左往右)
System.out.println(str1.lastIndexOf("lo"));
//返回指定子字符串在此字符串中最后一次出现处的索引,从指定的索引开始反向搜索
System.out.println(str1.lastIndexOf("lo", 5));
//注意:indexof和lastIndexof方法如果未找到都是返回-1
//什么情况下indexof和lastIndexof返回值相同:1、存在唯一的一个str 2、不存在
String str1 = "helloworld";
//替换字符或字符串
System.out.println(str1.replace('l', 'o'));
36、StringBuffer、StringBuilder
1.String、StringBuffer、StringBuilder三者的异同:
String:不可变的字符序列;
StringBuffer:可变的字符序列;线程安全,效率低;
StringBuilder:可变的字符序列,线程不安全,效率高;
底层都使用char[]存储
2.源码分析:
String str =new String();//char[] value = new char[0];
String str1 =new String(“abc”);//char[] value = new char[]{‘a’,‘b’,‘c’};
StringBuffer sb1 =new StringBuffer();//char[] value = new char[16];底层创建长度16的数组
sb1.length();//0,元素的个数
StringBuffer sb2 =new StringBuffer(“abc”);//char[] value = new char[“abc”.length()+16];
3.常用方法:
StringBuffer sb1 = new StringBuffer("abc");
//进行字符串的拼接
sb1.append(1);
System.out.println(sb1);
//删除指定位置的内容(左闭右开)
System.out.println(sb1.delete(2, 3));
//把[start,end)位置替换
System.out.println(sb1.replace(1, 3, "hello"));
//在指定位置插入
System.out.println(sb1.insert(2, "def"));
//将字符串反转
System.out.println(sb1.reverse());
37、包装类
1.基本数据类型转包装类:
int num = 1;
Integer int1 = new Integer(num);
2.包装类转基本数据类型:
Integer int1 = new Integer(1);
int num = int1.intValue();
3.自动装箱:
int num = 1;
Integer in1 = num2;
4.自动拆箱:
int num3 = in1;
5.byte-Byte
short-Short
int-Integer
long-Long
float-Float
double-Double
char-Character
boolean-Boolean
38、Date类
1.时间戳,具体到毫秒
long time = System.currentTimeMillis();//可用于生成订单号
2.Date类
java.sql.Date对应着数据库中的日期类型的变量
//创建一个对应当前时间的Date对象(java.util.Date;)
Date date1 = new Date();
//显示当前的年月日时分秒
System.out.println(date1.toString());
//获取当前Date对象对应的毫秒数
System.out.println(date1.getTime());
3.SimpleDateFormat
两个操作:(1)格式化:日期 转 字符串
(2)解析:格式化的逆过程, 字符串 转 日期
SimpleDateFormat sdf = new SimpleDateFormat();
//格式化:日期 转 字符串
Date da = new Date();
System.out.println(sdf.format(da));
//解析:格式化的逆过程,字符串 转 日期
String str = "20-8-15 上午10:18";
Date da1 = sdf.parse(str);
System.out.println(da1);
//按照指定的方式进行格式化和解析
SimpleDateFormat sdf1 = new SimpleDateFormat("yyyy-MM-dd hh:mm:ss");
String str2 = sdf1.format(da);
System.out.println(str2);
//解析:要求字符串必须是符合SimpleDateFormat识别的格式(通过构造器参数体现)
Date da2 = sdf1.parse("2020-08-15 10:27:36");
System.out.println(da2);
//将字符串“2020-8-15”转换为java.sql.Date
String birth = "2020-8-15";
SimpleDateFormat sdf2 = new SimpleDateFormat("yyyy-MM-dd");
Date da3 = sdf2.parse(birth);
java.sql.Date birthdate = new java.sql.Date(da3.getTime());
System.out.println(birthdate);
4.Calendar日历类(抽象类)
//1.实例化
//方式一:创建其子类的对象
//方式二:调用其静态方法getInstance();
Calendar calendar = Calendar.getInstance();
//get() 这个月的第几天
int days = calendar.get(Calendar.DAY_OF_MONTH);
System.out.println(days);
//set()
calendar.set(Calendar.DAY_OF_MONTH, 22);
days = calendar.get(Calendar.DAY_OF_MONTH);
System.out.println(days);
//add();
calendar.add(Calendar.DAY_OF_MONTH, 3);
days = calendar.get(Calendar.DAY_OF_MONTH);
System.out.println(days);
//getTime():日历类 转 Date
Date date = calendar.getTime();
System.out.println(date);
//setTime();Date 转 日历类
Date date1 = new Date();
calendar.setTime(date1);
days = calendar.get(Calendar.DAY_OF_MONTH);
System.out.println(days);
//注意
//获取月份时:一月是0,二月是1,以此类推,12月是11
//获取星期时:周日是1,周二是2, 周六是7
5.LocalDate、LocalTime、LocalDateTime(使用最多)的使用
//获取当前的日期
LocalDate localDate = LocalDate.now();
LocalTime localTime = LocalTime.now();
LocalDateTime localDateTime = LocalDateTime.now();
System.out.println(localDate);
System.out.println(localTime);
System.out.println(localDateTime);
//of() 设置指定的年、月、时、分、秒
LocalDateTime localDateTime2 = LocalDateTime.of(2020, 8, 15, 13, 21);
System.out.println(localDateTime2);
//getXXx() 获取相关的属性
System.out.println(localDateTime.getDayOfMonth());
//体现不可变性
//withXxx():设置相关的属性
LocalDateTime localDateTime3 = localDateTime.withDayOfMonth(22);
System.out.println(localDateTime);//当前天数
System.out.println(localDateTime3);//22
//月份加3
LocalDateTime localDateTime4 = localDateTime.plusMonths(3);
System.out.println(localDateTime4);
//天数减6
LocalDateTime localDateTime5 = localDateTime.minusDays(6);
System.out.println(localDateTime5);
6.Instant类
//now() 获取本初子午线对应的标准时间
Instant instant = Instant.now();
System.out.println(instant);
//添加偏移量 获取当时时间
OffsetDateTime offsetDateTime = instant.atOffset(ZoneOffset.ofHours(8));
System.out.println(offsetDateTime);
//获取毫秒数
System.out.println(instant.toEpochMilli());
7.DateTimeFormatter
DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd hh:mm:ss");
//格式化
String str = formatter.format(LocalDateTime.now());
System.out.println(str);
//解析
TemporalAccessor accessor = formatter.parse("2020-08-15 06:08:23");
System.out.println(accessor);
39.集合
1.集合框架的概述
(1)集合、数组都是对多个数据进行存储的操作的结构,简称Java容器。说明:此时的存储,主要指的是内存层面的存储,不涉及到持久化存储
(2)数组在存储多个数据方面的特点:一旦初始化以后,其长度就确定了。数组一旦定义好,其元素的类型也就确定了。我们也就只能操作指定类型的数据了
(2)数组在存储多个数据方面的缺点:一旦初始化以后,其长度不可修改。数组中提供的方法非常有限,对于增删改查等操作,非常不便,效率不高。数组存储的特点:有序、可重复。对于无序、不可重复需求,不能满足
2.集合框架
(1)Collection接口:单例集合,用来存储一个一个的对象
List接口:存储有序的、可重复的数据。(ArrayList、LinkedList、Vector)(底层实现使用数组)
Set接口:存储无序的、不可重复的数据(HashSet、LinkedHashSet、TreeSet),当向set中添加数据时,先调用hashcode的方法计算哈希值,决定set元素的位置,在通过equals方法比较两个对象是否相同。
HashSet是采用哈希算法存储集合的元素,底层使用的是哈希表。TreeSet是SortedSet的子类,可以确保集合元素处理排序的状态,TreeSet内部是二叉树,默认是从小到大排序。两种排序方法自然排序,定制排序
LinkedHashSet:他是HashSet的一个子类,是根据元素的hashcode值来决定元素存储位置,它也使用链表来维护元素顺序。
(2)Map接口:双列集合,用来存储一对(key–value)一对的数据(HashMap、LinkedHashMap、TreeMap、Hashtable、Properties)。key是不能重复的,使用set存储。value是可以重复的,使用collection存储的。Hashtable的子类properties
3.collection的常用方法
Collection coll = new ArrayList();
//add() 添加元素
coll.add(123);
coll.add("aa");
coll.add(new Date());
//size 元素个数
System.out.println(coll.size());
//addAll(Collection coll1) 将coll1集合的元素添加到当前的集合
Collection coll1 = new ArrayList();
coll1.add(123);
coll1.add(345);
coll.addAll(coll1);
//clear() 清空集合元素
coll.clear();
//isEmpty 判断当前集合是否为空
System.out.println(coll.isEmpty());
Collection coll2 = new ArrayList();
coll2.add(123);
coll2.add("aaa");
coll.add(new String("Tom"));
coll.add(new Person("Tom",22));
//contains(Object obj) 判断当前集合是否包含obj
//向collection接口实现类的对象添加数据obj时,要求obj所在类要重写equals()
System.out.println(coll2.contains(123));
System.out.println(coll2.contains(new String("Tom")));//true 调用equals方法比较
System.out.println(coll2.contains(new Person("Tom",22)));//true 调用equals方法比较
//containsAll(Collection coll) 判断形参coll1中的所有元素是否都在当前集合中
Collection coll3 = Arrays.asList(123,"aaa");
System.out.println(coll2.containsAll(coll3));
//remove() 从当前集合删除元素 boolean 删除成功true 反之false
coll2.remove(123);
//removeAll(Collection coll) 从当前结合移除coll的所有元素
coll2.removeAll(coll3);
//retainAll(Collection coll) 获取当前集合和coll集合的交集,并返回出来
coll2.retainAll(coll3);
//equals(Object obj) //当前集合和形参集合元素都相同时返回true
coll2.equals(coll3);
//hashCode 返回当前对象的哈希值
System.out.println(coll2.hashCode());
//集合 转 数组
Object[] arr = coll2.toArray();
//数组转集合
List<String> list = Arrays.asList(new String[]{"aa","bb","cc"});
4.使用Iterator遍历collection
Iterator iterator = coll2.iterator();
//判断是否还有下一个元素
while(iterator.hasNext()){
//指针下移。将下移以后集合位置上的元素返回
System.out.println(iterator.next());
}
//使用foreach()
for(Object obj:coll2){
System.out.println(obj);
}
5.List
ArrayList、LinkedList、Vector三者的异同
相同:都是实现List接口,存储数据的特点相同:存储有序的、可重复的数据
不同点:
ArrayList:作为List接口的主要实现类;线程不安全,效率高;底层使用Object[] elementData存储。底层使用的是一个可变数组,对随机访问效率高
LinkedList:对于频繁的插入,删除操作,使用此效率高;底层使用双向链表存储,访问速度比较慢
Vector:线程安全,效率低;底层使用Object[] elementData存储
6.ArrayList的源码分析:
ArrayList list = new ArrayList();//底层Object[] elementData初始化为{},并没有创建长度为10的数组
list.add(123);//第一次调用add()时,底层才创建长度为10的数组,并将数据添加到数组中
…
list.add(11);//如果此次的添加导致底层elementData数组容量不够,则扩容。默认情况下,扩容为原来的1.5倍,同时需要将原有数组中的数据赋值到新的数组中
7.LinkedList源码分析:
8.Set
(1)HashSet:作为Set接口的主要实现类;线程不安全;可以存储null值
(2)LinkedHashSet:作为HashSet的子类 ;遍历其内部数据的时候,可以按照添加的顺序遍历
(3)TreeSet:可以按照添加对象的指定属性,进行排序。
(4)Set的无序性:不等于随机性,存储的数据在底层数组中并非按照数组索引的顺序添加,而是根据哈系数添加
(5)不可重复性:保证添加的元素按照equals()判断时,不能返回true。相同的元素只能添加一个
(6)HashSet添加元素的过程:添加元素a,首先调用元素a所在类中的hashcode()方法,计算元素a的哈希值,此哈希值接着通过某种算法计算出HashSet底层数组中的存放位置(即为:索引位置),判断数组此位置上是否已经有元素了:如果此位置上没有其他元素,则元素a添加成功。如果此位置上有其他元素b,则比较a元素与元素b的hash值;如果hash值不相同,则元素a添加成功①。如果hash值相同,进而需要调用元素a所在类中equals方法:equals返回true,元素a添加失败。equals返回false,则元素a添加成功。②
对于添加成功的①和②,元素a与已经存在指定索引位置上数据以链表的方式存储
要求:向Set中添加数据,其所在类一定要重写hashCode和equals
(7)LinkedHashSet作为HashSet的子类,再添加数据的同时,每个数据还维护了两个引用,纪录此数据前一个数据和后一个数据。对于频繁的遍历,效率高于HashSet
(8)TreeSet:向TreeSet中添加的数据,要求相同类的对象。两种排序方式:自然排序和定制排序。自然排序中,比较两个对象是否相同的标准为:compareTo返回0,不在是equals。
public class Person implements Comparable {
//姓名大到小 年龄小到大
@Override
public int compareTo(Object o) {
if(o instanceof Person){
Person p = (Person)o;
int compare = -this.name.compareTo(p.name);
if(compare !=0){
return compare;
}else{
return Integer.compare(this.age, p.age);
}
}else{
throw new RuntimeException("输入的类型不匹配");
}
}
}
public static void main(String[] args) {
// TODO Auto-generated method stub
TreeSet tr = new TreeSet();
tr.add(new Person("aa",22));
tr.add(new Person("dd",21));
tr.add(new Person("aa",23));
tr.add(new Person("ee",25));
tr.add(new Person("cc",20));
Iterator iterator = tr.iterator();
while(iterator.hasNext()){
System.out.println(iterator.next());
}
}
9.Map
HashMap:作为map的主要实现类;线程不安全,效率高;存储null的key和value
LinkedHashMap:保证在遍历map元素时,可以按照添加的顺序实现遍历。在原有的HashMap底层结构基础上,添加了一对指针,指向前一个和后一个元素。对于频繁的遍历操作,此类执行效率高于HashMap
TreeMap:保证按照添加的key-value对进行排序,实现排序遍历。
Hashtable:作为古老的实现类;线程安全,效率低,不能存储null的key和value
Properties:常用来处理配置文件。key和value都是String类型
10.Map结构的理解
(1)Map中的key:无序的、不可重复的,使用set存储所有的key key所在的类要重写equals和hashCode
(2)Map中的value:无序的、可重复的,使用collection存储所有的value value所在的类要重写equals
(3)一个键值对:key-value构成了一个Entry对象。Map中的entry:无序的、不可重复的,使用Set存储所有的entry
11.HashMap的底层实现原理 jdk7
HashMap map = new HashMap();
在实例化以后,底层创建了长度是16的一维数组Entry[] table。
map.put(key1,value1):
首先,调用key1所在雷翔的hashcode()计算key1哈希值,此哈希值经过某种算法计算以后,得到早Entry数组中的存放位置。
如果此位置上的数据为空,此时的key1-value1添加成功①
如果此位置上的数据不为空(意味着此位置上存在一个或多个数据,链表形式存在),比较key1和已经存在的一个或多个数据的哈希值:
如果key1的哈希值与已经存在的数据哈希值不相同,此时key1-value1添加成功②
如果key1的哈希值和已经存在的某一个数据(key2-value2)的哈希值相同,继续比较:调用key1所在类的equals(key2) 如果equals返回false:此时key1-value1添加成功③ 如果equals返回true:使用value1替换value2
补充:关于②和③;此时key1-value1和原来的数据以链表的形式存储
在不断添加过程中,会涉及到扩容问题,当超出临界时(且要存放的位置非空时),扩容,默认的扩容方式;扩容为原来的容量2倍,并将原有的数据复制过来
jdk8相较于7的不同:
(1)new HashMap():底层没有创建一个长度为16的数组
(2)jdk8底层的数组是:Node[]而非Entry[];
(3)首次调用put方法,底层创建长度为16的数组
(4)jdk7底层结构只有:数组+链表。jdk8底层结构:数组+链表+红黑树
12.Map常用方法
Map map = new HashMap();
//添加
map.put("aa", 123);
map.put("bb", 123);
map.put("cc", 123);
map.put("dd", 123);
map.put("ee", 123);
Map map1 = new HashMap();
map1.put("AA", 123);
//添加
map.putAll(map1);
//remove
map.remove("AA");
//clear //清空数据
map.clear();
//get 获取指定key对应的数据
map.get("AA");
//contains 是否包含指定value 或key
map.containsKey("AA");
map.containsValue(123);
//size
map.size();
//判断是否为空
map.isEmpty();
//equals 两个map存储的数据是否完全相同
//遍历所有的key集:keySet
Set set = map.keySet();
Iterator iterator = set.iterator();
while(iterator.hasNext()){
System.out.println(iterator.next());
}
//遍历所有的values
Collection values = map.values();
for(Object obj:values){
System.out.println(obj);
}
//遍历所有的key-value
Set entrySet = map.entrySet();
Iterator iterator2 = entrySet.iterator();
while(iterator2.hasNext()){
Object obj2 = iterator2.next();
//entrySet集合中的元素都是entry
Map.Entry entry = (Map.Entry)obj2;
System.out.println(entry.getKey()+" "+entry.getValue());
}
Comparable接口:
1、位于java.lang包下
2、实现的原理基于红黑二叉树原理(小的在左边,大的在右边)。
3、包含一个比较方法compareto(T t);字符串根据字典顺序进行比较。自定义类型:需要实现Comparable接口,重写compareTo()方法,自定义实现排序规则。
public class Person implements Comparable<Person>{
@Override
public int compareTo(Person p) {
int num = this.age - p.age;
if(num==0){
return this.name.compareTo(p.name);
}
return num;
}
List<Person> persons = new ArrayList<Person>();
persons.add(new Person(20,"t"));
persons.add(new Person(23,"a"));
persons.add(new Person(23,"b"));
persons.add(new Person(22,"f"));
persons.add(new Person(25,"e"));
//排序
Collections.sort(persons);
Comparator接口
1、Comparator接口也是一个排序比较器,位于java.util包下。
2、包含一个比较方法compare(T t1,T t2 )
3、如果一个类实现这个接口,一定要实现compare方法,但不一定实现equals方法
public class Student implements Comparator<Student> {
@Override
public int compare(Student s1, Student s2) {
if(s1.age-s2.age == 0){
return s1.name.compareTo(s2.name);
}
return s1.age-s2.age;
}
}
List<Student> students = new ArrayList<>();
students.add(new Student(20,"t"));
students.add(new Student(23,"a"));
students.add(new Student(23,"b"));
students.add(new Student(22,"f"));
students.add(new Student(25,"e"));
//第一种 匿名实现类
Collections.sort(students,new Comparator<Student>() {
@Override
public int compare(Student s1, Student s2) {
if(s1.getAge()-s2.getAge()==0){
return s1.getName().compareTo(s2.getName());
}
return s1.getAge()-s2.getAge();
}
});
//第二种 提供实现类对象
Collections.sort(students,new Student());
40.泛型
泛型:1.泛型是jdk1.5之后的安全机制。2.避免了强制类型转换引发的异常。3.限制了数据类型,保护了数据类型的安全。4.将运行期间引发的异常转到了编译期间
List<Integer> array = new ArrayList<Integer>();
array.add(123);
Map<String, Integer> map = new HashMap<String, Integer>();
map.put("AA", 123);
Set<Map.Entry<String, Integer>> entry = map.entrySet();
Iterator<Map.Entry<String, Integer>> iterator = entry.iterator();
while(iterator.hasNext()){
Map.Entry<String, Integer> e = iterator.next();
System.out.println(e.getKey()+" "+e.getValue());
}
1.自定义泛型
//自定义泛型类
public class Order<T> {
private String orderName;
private int orderId;
T orderT;
//泛型方法:在方法中出现了泛型结构,泛型参数与类的泛型参数没有任何关系
//泛型方法所属的类是不是泛型方法都没有关系
//泛型方法,可以声明为静态的。原因:泛型参数是在调用方法时确定的。并非在实例化类时确定的。
public <E> List<E> copyFromArrayToList(E[] arr){
ArrayList<E> list = new ArrayList<>();
for(E e:arr){
list.add(e);
}
return list;
}
}
public static void main(String[] args) {
// TODO Auto-generated method stub
//实例化时指明类的泛型
Order<String> order1 = new Order<String>("AA",123,"bb");
order1.setOrderT("cc");
//实现泛型方法
String[] arr = new String[]{"aa","bb"};
System.out.println(order1.copyFromArrayToList(arr));
}
注意:(1)静态方法中不能使用泛型 (2)虽然类A是类B的父类,但是G《A》和G《B》二者不具备子父类关系,二者是并列关系
2.通配符 ?
类A是类B的父类,但是G《A》和G《B》是没有关系的,二者共同的父类是:G《?》
List<Object> list1 = null;
List<String> list2 = null;
List<?> list = null;//不能向其内部添加数据
41、内部类
public class InnerClassTest {
//实例化成员内部类的对象
//创建Dog实例(静态成员内部类)
Person.Dog dog = new Person.Dog();
//创建非静态内部类实例
Person p = new Person();
Person.Cat cat = p.new Cat();
}
class Person{
//静态成员内部类
static class Dog{
}
//成员内部类
class Cat{
}
public void method(){
//局部内部类
class AA{
}
}
{
//局部内部类
class BB{
}
}
public Person(){
class CC{
}
}
}
42、异常
1.异常的体系结构:
java.lang.Throwable
----java.lang.error:一般不编写针对性的代码进行处理
----java.lang.Exception:可以进行异常处理
--------编译时异常(checked)
-------------IoException:输入或输出异常
------------------FileNotFoundException:指示尝试打开由指定路径名表示的文件失败
-------------ClassNotFoundException:
--------运行时异常(checked)
-------------NullPointerException:空指针异常
-------------ArrayIndexOutOfBoundsException:角标越界
-------------ClassCastException:类型转换异常
-------------NumberFormatException:数字格式异常
-------------InputMismatchException:输入不匹配
-------------ArithmeticException:算数异常
2.异常的处理:抓抛模型
(1)抛:程序在正常执行过程中,一旦出现异常,就会在异常处生成一个对应异常类的对象,并将此对象抛出。一旦抛出对象以后,其后代码就不再执行
(2)抓:异常的处理方式 ①try-catch ②throws
3.开发中,由于运行时异常比较常见,所以我们通常就不针对运行时异常编写try-catch-finally了,针对编译时异常,一定要考虑异常的处理
(1)try-catch-finally
public class TestException {
public static void main(String[] args) {
// TODO Auto-generated method stub
String str = "abc";
try{
int num = Integer.parseInt(str);
}catch(NumberFormatException e){
e.printStackTrace();
}catch(Exception e){
e.printStackTrace();
}
System.out.println(method());
}
public static int method(){
String str = "abc";
try{
int num = Integer.parseInt(str);
return 1;
}catch(NumberFormatException e){
e.printStackTrace();
return 2;
}catch(Exception e){
e.printStackTrace();
return 3;
//像数据库连接、输入输出流、网络编程Socket等资源,JVM是不能自动回收的,我们需要自己手动的
//进行资源的释放,此时的资源释放,就需要声明在finally中
}finally{
System.out.println("我执行完,再返回值");
}
}
}
(2)throws+异常类型写在方法声明处。指明此方法执行时,可能会抛出的异常类型。一旦方法体执行时,出现异常,仍会在异常代码处生成一个异常类的对象,此对象满足throws后异常类型时,就会被抛出。异常代码后续的代码 ,就不在执行。
(3)try-catch-finally:真正将异常处理掉了。throws的方式只是将异常抛给方法的调用者。并没有真将异常处理掉。
(4)子类重写的方法抛出的异常类型不大于父类被重写的方法抛出的异常类型
(5手动生成一个异常对象并抛出
public class TestException2 {
public static void main(String[] args) {
// TODO Auto-generated method stub
Student stu = new Student();
try {
stu.register(-1);
System.out.println(stu);
} catch (Exception e) {
// TODO Auto-generated catch block
System.out.println("您输入的数据非法!");
}
}
}
class Student{
private int id;
public void register(int id){
if(id>0){
this.id = id;
}else{
throw new RuntimeException("您输入的数据非法!");
}
}
}
43、I/O流
1.File类
常用方法:
File file = new File("hello.txt");//绝对路径
File file2 = new File("D:\\io\\hi.txt");
//获取绝对路径
System.out.println(file.getAbsolutePath());
//获取路径
System.out.println(file.getPath());
//获取上层文件目录路径
System.out.println(file.getParent());
//获取文件长度
System.out.println(file.length());
//获取最后一次的修改时间,毫秒值
System.out.println(file.lastModified());
//获取指定目录下的所有文件或者文件目录的名称数组
File file3 = new File("D:\\迅雷下载");
String[] list = file3.list();
for(String s:list){
System.out.println(s);
}
//获取指定目录下的所有文件或者文件目录的File数组
File[] files = file3.listFiles();
for(File f:files){
System.out.println(f);
}
//把文件重命名为指定的文件路径
//返回true 需要file在硬盘是存在的,其file2不能在硬盘中存在
System.out.println(file.renameTo(file2));
//判断是否是文件目录
System.out.println(file.isDirectory());
//判断是否是文件
System.out.println(file.isFile());
//判断是否存在
System.out.println(file.exists());
//判断是否可读
System.out.println(file.canRead());
//判断是否可写
System.out.println(file.canWrite());
//判断是否隐藏
System.out.println(file.isHidden());
//文件的创建
File file4 = new File("hi.txt");
if(!file4.exists()){
file4.createNewFile();
System.out.println("创建成功");
}else{
file4.delete();
System.out.println("删除成功");
}
//文件目录的创建
File file5 = new File("d:\\io\\io1\\io2");
//创建文件目录,若此文件的上层目录文件不存在,无法创建
file5.mkdir();
//创建文件目录,若此文件的上层目录文件不存在,一并创建
file5.mkdirs();
2.I/O流
流的分类:
(1)操作数据单位:字节流、字符流
(2)数据流向:输入流、输出流
(3)流的角色:节点流、处理流
流的体系结构
抽象基类:InputStream、OutputStream、Reader、Writer
节点流或文件流:FileInputStream、FileOutputStream、FileReader、FileWriter
缓冲流(处理流的一种):BufferedInputStream、BufferedOutputStream、BufferedReader、BufferedWriter
FileReader的使用
FileReader fr = null;
try {
//实例化File类对象,指明要操作的文件
File file = new File("hello.txt");
//提供具体的流
fr = new FileReader(file);
//数据的读入 达到末尾返回-1
int data = fr.read();
while(data!=-1){
System.out.print((char)data);
data = fr.read();
}
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}finally{
try {
//关闭流
if(fr!=null)
fr.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
FileReader(char[] cubf)
FileReader fr = null;
try {
File file = new File("hello.txt");
fr = new FileReader(file);
char[] arr = new char[5];
int len;
while((len = fr.read(arr))!=-1){
for(int i=0;i<len;i++){
System.out.print(arr[i]);
}
}
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}finally{
try {
fr.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
FileWriter
FileWriter fw = null;
try {
File file = new File("hello1.txt");
fw = new FileWriter(file,true);//true追加写入 false覆盖原有文件写入
/*Scanner sc = new Scanner(System.in);
String str = sc.next();*/
fw.write("hello world");
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}finally{
try {
fw.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
FileReader和FileWriter结合使用 文件复制
FileReader fr = null;
FileWriter fw = null;
try {
File file = new File("hello.txt");
File file2 = new File("hello2.txt");
fr = new FileReader(file);
fw = new FileWriter(file2);
char[] arr = new char[5];
int len;
while((len = fr.read(arr))!=-1){
fw.write(arr,0,len);
}
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}finally{
if(fr!=null){
try {
fr.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
if(fw!=null){
try {
fw.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
FileInput和OutputStream实现非文本文件的复制
File file = new File("photo.jpg");
File file2 = new File("photo2.jpg");
FileInputStream fi = null;
FileOutputStream fo = null;
try {
fi = new FileInputStream(file);
fo = new FileOutputStream(file2);
byte[] buffer = new byte[5];
int length;
while((length = fi.read(buffer))!=-1){
fo.write(buffer,0,length);
}
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}finally {
if(fi!=null){
try {
fi.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
if(fo!=null){
try {
fo.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
处理流:BufferedInputStream
提高流的读取、写入速度。加入了一个缓冲区。
BufferedInputStream bis = null;
BufferedOutputStream bos = null;
try {
/**
File file = new File("photo.jpg");
File file2 = new File("photo3.jpg");
FileInputStream fi = new FileInputStream(file);
FileOutputStream fo = new FileOutputStream(file2);
bis = new BufferedInputStream(fi);
bos = new BufferedOutputStream(fo);
**/
bis = new BufferedInputStream(new FileInputStream(new File("photo.jpg")));
bos = new BufferedOutputStream(new FileOutputStream(new File("photo3.jpg")) );
byte[] buffer = new byte[5];
int length;
while((length = bis.read(buffer))!=-1){
bos.write(buffer,0,length);
bos.flush();//刷新缓冲区
}
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}finally {
//外层流关闭的同时内层流也关闭了,可以省略
if(bis!=null){
try {
bis.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
if(bos!=null){
try {
bos.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
BufferedReader
BufferedReader br = null;
BufferedWriter bw = null;
try {
br = new BufferedReader(new FileReader(new File("hello.txt")));
bw = new BufferedWriter(new FileWriter(new File("hello3.txt")));
String data;
//readLine 读取一行
while((data=br.readLine())!=null){
bw.write(data);
bw.newLine();//换行
}
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}finally {
if(br!=null){
try {
br.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
if(bw!=null){
try {
bw.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
处理流之二:转换流的使用
(1)转换流:属于字符流 InputStreamReader:将一个字节的输入流转换为字符的输入流。OutputStreamReader:将一个字符的输出流转换为字节的输出流
(2)作用:提供字节流与字符流之间的转换
(3)解码:字节、字节数组 转为 字符数组、字符串。编码:字符数组、字符串 转为 字节、字节数组
InputStreamReader sr = null;
try {
FileInputStream fi = new FileInputStream("hello.txt");
sr = new InputStreamReader(fi,"UTF-8");//取决于文件保存时使用的字符集
char[] cbuf = new char[5];
int len;
while((len=sr.read(cbuf))!=-1){
String str = new String(cbuf,0,len);
System.out.print(str);
}
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}finally{
if(sr!=null){
try {
sr.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
对象的序列化
对象序列化机制:允许把内存中的java对象转换成平台无关的二进制流 ,从而允许把这种二进制流持久的保存在磁盘上,或通过网络将这种二进制流传输到另一个网络节点。当其他程序获取这种二进制流,就可以恢复成原来的java对象
objectoutputStream不能序列化staic和transient修饰的成员变量
//Person满足如下条件,方可序列化
public class Person implements Serializable {
public static final long serialVersionUID = 478515256215L;
}
ObjectOutputStream oos = null;
try {
oos = new ObjectOutputStream(new FileOutputStream("object.dat"));
oos.writeObject(new Person("张三",22));
oos.flush();
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}finally{
if(oos!=null){
try {
oos.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
ObjectInputStream ois = null;
try {
ois = new ObjectInputStream(new FileInputStream("object.dat"));
Object obj = ois.readObject();
Person p =(Person)obj;
System.out.println(p);
} catch (FileNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (ClassNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}finally{
if(ois!=null){
try {
ois.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
45、多线程
程序:程序存储在磁盘上,包含可执行机器指令和数据的静态实体(静态代码)
进程:是系统进行资源分配和调度的基本单位,有自己的生命周期(动态)
线程:是进程的一条执行路径。一个进程至少有一个线程,进程中的多个线程共享进程的资源
多线程:在单个程序中同时运行对个线程完成不同的工作,称为多线程
(1)用户向服务器发送一个请求就是一个线程
(2)客户端N多的请求同时去请求服务器这是多线程
线程的两种调度方式:抢占式调度和非抢占式调度
进程和线程的区别:
(1)一个程序至少有一个进程,一个进程至少一个线程(qq程序可以打开多个,一个qq可以发送多条信息)
并行和并发:
并发:(1)一个处理器同时处理多个内容(一个人同时去吃三个馒头)
(2)并发强调在一个时间段同时执行而一个时间段有多个单位时间,所以并发的多个任务在单位时间内不一定同时进行
并行:(1)多个处理器同时处理多个不同的任务(三个人同时去吃一个馒头)
(2)多个处理器或多台机器同时执行一段处理逻辑,是真正的同时
为什么要使用多线程:
(1)提升CPU的计算能力,避免资源浪费
(2)提升系统的响应速度
共享资源:
(1)允许被不同的线程访问的资源
(2)共享资源是多线程允许不只一个线程访问的类变量或者实例变量
锁:当多个线程共同使用一个共享资源时,或出现资源争抢,锁就是来解决这种冲突保证线程有序使用共享资源
线程安全:如果一段代码可以保证多个线程访问的时候正确操作共享资源,那么就是线程安全的
1.创建多线程,继承Thread类
class MyThread extends Thread{
@Override
public void run() {
// TODO Auto-generated method stub
for(int i=0;i<100;i++){
if(i%2==0){
System.out.println(i);
}
}
}
}
public class TestThread {
public static void main(String[] args) {
// TODO Auto-generated method stub
/*MyThread mt = new MyThread();
mt.start();
System.out.println("hello");
*/
new Thread(){
@Override
public void run() {
// TODO Auto-generated method stub
for(int i=0;i<100;i++){
if(i%2==0){
System.out.println(i);
}
}
}
}.start();
}
}
2.Thread中常用的方法
(1)start():启动当前线程;调用当前线程的run方法
(2)run():通常需要重写Thread类中的此方法,将创建的线程要执行的操作声明在此方法中
(3)currentThread():静态方法,返回执行当前代码的进程
(4)getName():获取当前线程的名字
Thread.currentThread.getName
(5)setName():设置当前线程的名字
(6)yield():释放当前CPU的执行权
(7)join():在线程a中调用线程b的join(),此时线程a就进入阻塞状态,直到线程b完全执行完以后,线程a才结束阻塞状态
(8)sleep(Long milltime)让当前进程睡眠一定时间
(9)isAlive:判断当前线程是否存活
3.线程的优先级
MAX_PRIORITY:10
MIN_PRIORITY:1
NORM_PRIORITY:5 默认优先级
getPriority():获取线程的优先级
setPriority(int p):设置线程的优先级
说明:高优先级的线程抢占低优先级线程的CPU的执行权。但是只是从概上讲,高优先级的线程高概率的情况下被执行。并不意味着只有高优先级的线程执行完以后,低优先级的线程才执行。
4.创建多线程的方式?:实现Runnable接口
class MyThread2 implements Runnable{
@Override
public void run() {
// TODO Auto-generated method stub
for(int i=0;i<100;i++){
if(i%2==0){
System.out.println(i);
}
}
}
}
public class TestThread2 {
public static void main(String[] args) {
// TODO Auto-generated method stub
MyThread2 myThread2 = new MyThread2();
Thread thread = new Thread(myThread2);
thread.start();
}
}
5.线程的生命周期
6.线程同步
三个窗口买票
线程安全问题:当某个线程操作车票过程中,尚未完成操作时,其他线程也参与进来,也操作车票,出现安全问题。
解决方式一:同步代码块
synchronized(同步监视器){
//需要被同步的代码
}
说明:(1)操作共享数据的代码,即为需要被同步的代码
(2)共享数据:多个线程共同操作的变量。例如ticket
(3)同步监视器:俗称“锁”。任何一个类的对象,都可以充当锁。多个线程必须公用一把锁。
class MyThread3 implements Runnable{
private int ticket = 100000;
//Object obj = new Object();
@Override
public void run() {
// TODO Auto-generated method stub
while(true){
synchronized(this){
if(ticket>0){
System.out.println(Thread.currentThread().getName()+"已售出"+ticket+"号票");
ticket--;
}else{
break;
}
}
}
}
}
public class TestThread3 {
public static void main(String[] args) {
// TODO Auto-generated method stub
MyThread3 mT3 = new MyThread3();
Thread t1 = new Thread(mT3);
Thread t2 = new Thread(mT3);
Thread t3 = new Thread(mT3);
t1.setName("1号窗口");
t2.setName("2号窗口");
t3.setName("3号窗口");
t1.start();
t2.start();
t3.start();
}
}
方式二:同步方法
class MyThread5 implements Runnable{
private int ticket = 100000;
@Override
public void run() {
// TODO Auto-generated method stub
while(true){
show();
}
}
public synchronized void show(){
if(ticket>0){
System.out.println(Thread.currentThread().getName()+"已售出"+ticket+"号票");
ticket--;
}
}
}
public class TestThread4 {
public static void main(String[] args) {
// TODO Auto-generated method stub
MyThread5 mT5 = new MyThread5();
Thread t1 = new Thread(mT5);
Thread t2 = new Thread(mT5);
Thread t3 = new Thread(mT5);
t1.setName("1号窗口");
t2.setName("2号窗口");
t3.setName("3号窗口");
t1.start();
t2.start();
t3.start();
}
}
方式三:Lock锁
(1)synchronized和lock的异同
同:二者都可以解决线程安全问题
异:synchornized机制在执行完相应的同步代码以后,自动释放同步监视器。lock需要手动的启动同步,同时结束同步也需要手动的实现
class MyThread6 implements Runnable{
private int ticket = 1000;
private ReentrantLock lock = new ReentrantLock();
@Override
public void run() {
// TODO Auto-generated method stub
while(true){
try{
lock.lock();
if(ticket>0){
System.out.println(Thread.currentThread().getName()+"已售出"+ticket+"号票");
ticket--;
}else{
break;
}
}finally{
lock.unlock();
}
}
}
}
public class TestThread5 {
public static void main(String[] args) {
// TODO Auto-generated method stub
MyThread6 mT6 = new MyThread6();
Thread t1 = new Thread(mT6);
Thread t2 = new Thread(mT6);
Thread t3 = new Thread(mT6);
t1.setName("1号窗口");
t2.setName("2号窗口");
t3.setName("3号窗口");
t1.start();
t2.start();
t3.start();
}
}
线程通信:两个线程交替打印
wait():一旦执行此方法,当前线程进就入阻塞状态,并释放同步监视器
notify():执行此方法就会唤醒被wait的一个线程。多个线程被wait,就唤醒优先级高的
notifyall():唤醒所有被wait的线程
class Number implements Runnable{
private int number=1;
@Override
public void run() {
// TODO Auto-generated method stub
while(true){
synchronized (this) {
notify();
if(number<=100){
try {
Thread.sleep(10);
} catch (InterruptedException e1) {
// TODO Auto-generated catch block
e1.printStackTrace();
}
System.out.println(Thread.currentThread().getName()+"打印"+number);
number++;
try {
wait();
} catch (InterruptedException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}else{
break;
}
}
}
}
}
public abstract class TestThread7 {
public static void main(String[] args) {
// TODO Auto-generated method stub
Number num = new Number();
Thread t1 = new Thread(num);
Thread t2 = new Thread(num);
t1.setName("线程1");
t2.setName("线程2");
t1.start();
t2.start();
}
}
sleep和wait的异同
相同点:一旦执行,都可以使得当前的线程进入阻塞状态
不同:(1)两个方法声明的位置不同:Thread类中声明sleep(),Object类中声明wait()
(2)调用的要求不同:sleep可以再任何需要的场景下调用,wait必须使用在同步代码块中
(3)关于是否释放同步监视器:如果两个方法都使用在同步代码块或同步方法中,sleep不会释放后锁,wait会释放锁
实现多线程的另外两种方式:
(1)实现Callable接口
(2)使用线程池
46、反射
1.java中的编译类型
静态编译:在编译时确定类型,绑定对象即通过
动态编译:在运行时确定对象类型,绑定对象,体现了java的灵活性,多态性。减少类之间的耦合
2.java识别对象和类信息的方式
反射机制:允许运行期间发现和使类的信息 前提:Class对象(代表字节码的Class)
Class:java中一个类 相当于一个探头,将一个类的方法,变量等其他信息告诉运行程序
3.什么是反射
java程序在运行时,对于任何一个类都能获取这个类的信息(属性、方法、修饰符),还可以在运行时实例化对象,调用方法及设置属性
一个类中有成员变量、方法、构造器等信息,利用反射可以对此类进行解剖,把各个组成部分映射成一个个对象。
4.为什么需要使用反射
使用反射可以赋予JVM动态的编译能力,否则类的元数据信息只能通过静态编译的来实现,当我们写的程序在运行时,需要动态的加载,提升服务器的性能
eg:项目开发中使用的数据库是mysql或者Oracle,需要动态的根据实际情况加载相应的数据库驱动类
eg:spring中配置各种Javabean,是以xml形式的配置文件来存放,使用到那些类就通过spring容器会根据你的需求动态加载对应的类
使用java的反射机制可以增加程序的灵活性,避免将程序写死
5.实现反射的类
实现反射的类位于java.lang.reflect包中
Class类:表示为一个类
Method:表示为类的方法
Field类:表示类的成员属性
Constructor:表示为类的构造器
6.Class类
java.lang.Class:反射的源头
创建一个类,首先通过编译生成对应的class文件,之后通过JVM的类加载器(ClassLoader),将class文件加载到JVM内存中
此时会创建一个Class对象。Class对象只会加载一次
有了此clas对象后可以进行如下的操作:
①创建对应运类的对象
②调用对应的运行类指定的结构(属性、方法、构造器)
//获取Class对象的方式
//1、调用运行时类本身的class属性
Class clazz = Person.class;
//2.通过类对象的getclass()方法
Person p = new Person();
Class clazz2 = p.getClass();
//3.通过forName()静态方法
Class clazz3 = Class.forName("com.hpe.reflect.Person");
//反射之后对于Person的操作
Class clazz = Person.class;
//通过反射创建Person类对象
Constructor cons = clazz.getConstructor(String.class,int.class);
Object obj = cons.newInstance("Tom",12);
Person p = (Person)obj;
System.out.println(p.toString());
//通过反射,调用对象指定属性、方法
//调用属性
Field age = clazz.getDeclaredField("age");
age.set(p, 10);
System.out.println(p.toString());
//调用方法
Method show = clazz.getDeclaredMethod("show");
show.invoke(p);
//调用Person类的私有结构
//调用私有构造器
Constructor cons1 = clazz.getDeclaredConstructor(String.class);
cons1.setAccessible(true);
Person p1 = (Person)cons1.newInstance("Jack");
System.out.println(p1);
//调用私有的属性
Field name = clazz.getDeclaredField("name");
name.setAccessible(true);
name.set(p1, "Lilei");
System.out.println(p1);
//调用私有的方法
Method showNation = clazz.getDeclaredMethod("showNation", String.class);
showNation.setAccessible(true);
String nation = (String)showNation.invoke(p1, "中国");
System.out.println(nation);
关于java,lang.Class的理解
(1)类的加载过程
程序经过javac.exe.命令以后,会生成一个或多个字节码文件(.class结尾)。接着我们使用java.exe命令对某个字节码文件进行解释运行。相当于将某个字节码文件加载到内存中。此过程就称为类的加载,加载到内存中类,我们就成为运行时类,此运行时类,就称为Class的一个实例
2.创建运行时类的对象
Class<Person> clazz = Person.class;
/*
newInstance():调用此方法,创建对应的运行时类对象。内部调用了运行时类的空参构造器
此方法创建运行时类的对象的要求
(1)运行时类必须提供空参的构造器
(2)空参的构造器的访问权限得够 public
*/
Person obj = calzz.newInstance();
47、函数式编程
什么是函数式编程?
一切皆函数,将处理问题的粒度从类变成函数,将函数作为参数传递给另一个函数,简化代码的操作(有一些学生,筛选出男生,写一个filter方法,然后再筛选出前三个学生,写一个limit方法,就可以将filter函数作为参数传递给limit函数)
关于Lambda表达式的理解?
一种新的语法形式,简化了开发的编码操作,可以理解成函数式编程的体现
语法结构:->
(parameters) -> expression|{statements};
List<String> names = Arrays.asList("jack","tom","tina","cinda");
names.foreach((name)->System.out.print(name));
//使用双冒号操作符
names.foreach(System.out::println);
//查询集合中带有tina的元素
names.foreach(item ->{
if("tina".equals(item)){
System.out.println(item);
}
})
48、java程序设计模式
一、什么是设计模式
设计模式是软件开发过程中,能够增加系统的健壮性、可扩展性、可维护的一种设计模式或者一种软件设计思想
设计模式不是框架。框架大于设计模式
二、常见的设计模式
1.单例模式:一个类只允许创建一个实例对象。分类:饿汉式和懒汉式
饿汉式:构造方法私有化,无法直接实例化对象,只能通过static方法取得实例化对象。线程安全
懒汉式:当第一次访问的时候才会为期产生实例化对象操作。线程不安全
饿汉式和懒汉式的区别:
①饿汉式是线程安全的,懒汉式是线程不安全的
②饿汉式是空间换时间,当类装载的时候就会创建实例,每次调用的时候无需再做判断节省了运行时间。
懒汉式是时间换空间,体现了缓存的思想,刚开始不加载资源,等使用的时候才会加载
3.单例模式的使用场景
①充当共享数据:例如web页面的计数器
②防止创建多个对象过度消耗资源:多次访问IO或者数据库等资源
//饿汉式
public class Singleton {
//创建私有的实例化对象,在类的内部可以访问私有的属性
private static Singleton instance = new Singleton();
//私有化构造器
private Singleton(){
}
/**
* 获取对象的实例
* @return
*/
public static Singleton getInstance(){
return instance;
}
}
//懒汉式
public class Singleton2 {
private static Singleton2 instance = null;
private Singleton2(){
}
public static Singleton2 getInstance(){
if(instance == null){
synchronized (Singleton2.class) {
if(instance == null){
instance = new Singleton2();
}
}
}
return instance;
}
}
Singleton singleton1 = Singleton.getInstance();
Singleton singleton2 = Singleton.getInstance();
System.out.println(singleton1==singleton2);//true
Singleton2 singleton3 = Singleton2.getInstance();
Singleton2 singleton4 = Singleton2.getInstance();
System.out.println(singleton3==singleton4);//true
二、工厂模式:简单工厂、工厂方法、抽象工程
简单工厂
1、介绍:建立一个工厂类,对实现同一个接口的一些类进行实例的创建。不需要关注产品本身被创建的细节,只需要通过工厂就可以获得实例的对象
2、主要角色
①工厂
②抽象产品
③具体产品
49、网络编程
一、网络编程:编写程序使得多个设备或者多台计算机之间进行数据传输。
二、TCP和UDP
1.TCP和UDP是传输层的两个重要协议
2.TCP是连接的协议,TCP连接分为三个阶段:连接建立、数据传送、连接释放类似于打电话
3.UDP是非连接的协议,传输数据之前原始主机(源端)和目标主机(终端)不需要建立连接。类似于短信
三、HTTP
1.一种应用层协议,他是基于TCP协议纸上的请求响应式协议
2.HTTP指定客户端和服务器端如何建立连接,客户端如何从服务器请求数据,以及服务器端如何响应数据。
四、端口号:标记计算机上进程服务的端口
0-1023:常规的端口 1024-65535:自定义的服务端口
5、Socket编程
Socket:套接字 两台主机之间逻辑连接的端点。如果想要实现多个网络节点通信需要依赖于TCP/IP协议和HTTP协议
TCP/IP协议是传输层协议,主要解决数据如何在网络中传输
HTTP协议是应用层协议,主要解决如何包装数据
Socket编程实际上是对TCP/IP协议的封装,Socket本身不是协议,而是一个调用的接口(API)
Socket编程主要涉及到客户端和服务端的代码,首先在服务端创建一个服务器套接字(ServerSocket),并把它附加到一个端口上,服务器从这个端口监听连接。端口的选择1024-65535 选择一个没有被其他进程使用的端口
6、网络通信的第一要素:IP
InetAdderss:代表IP地址 一个对象就代表一个IP
创建InetAddress对象 getByName(String host)
//创建一个InetAddress对象
InetAddress inet = InetAddress.getByName("www.baidu.com");
System.out.println(inet);
//获取域名
System.out.println(inet.getHostName());
//获取IP地址
System.out.println(inet.getHostAddress());
//获取本机的IP地址
InetAddress localhost = InetAddress.getLocalHost();
System.out.println(localhost.getHostName());
System.out.println(localhost.getHostAddress());
ublic class TestTCP1 {
@Test
public void clent(){
//1、创建一个Scoket对象,向服务器发出套接字请求,通过构造器指明服务器的IP地址,以及接受程序的端口号
Socket socket = null;
//2、发送数据getOutPutStream()
OutputStream os = null;
try {
socket = new Socket(InetAddress.getByName("127.0.0.1"),9090);
os = socket.getOutputStream();
//3、写数据
os.write("你就是个二傻子".getBytes());
} catch (UnknownHostException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
//4、关闭相应的流和Socket对象
if(os!=null){
try {
os.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
if(socket!=null){
try {
os.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
@Test
public void server(){
//1、创建一个ServerSocket对象,指定服务的端口号,用于监听客户端的请求
ServerSocket ss = null;
//2、调用accept()方法,接受客户端的连接请求 返回一个socket对象
Socket s = null;
//3、获取客户端发送过来的请求数据,使用getInputStream()获取输入流
InputStream is = null;
try {
ss = new ServerSocket(9090);
s = ss.accept();
is = s.getInputStream();
//4、对获取的数据进行操作
byte[] b = new byte[20];
int len;
while((len=is.read(b))!=-1){
String str = new String(b,0,len);
System.out.println(str);
}
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
//获取客户端的IP
System.out.println("收到来自:"+s.getInetAddress().getHostAddress()+"的链接");
//5、释放资源
if(is!=null){
try {
is.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
if(s!=null){
try {
s.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
if(ss!=null){
try {
ss.close();
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
}
}