记录java学习,慢慢更新中
(图片懒得放了qaq)
文章目录
- 一、基础知识
- 二、Java基础
- (一)Java基本语法
- (二)面向对象OOP
- 2.1 java类及类的成员
- 2.1.1 java类及类的成员:属性、方法、构造器、代码块、内部类
- 2.1.2 JVM虚拟机
- 2.1.3 属性(成员变量)和局部变量
- 2.1.4 匿名对象
- 2.1.5 重载overload **(同一类中)**
- 2.1.6 重写override **(子类继承父类)**
- 2.1.7 可变个数的形参 (代替数组形参 )
- 2.1.8 private的使用
- 2.1.9 构造器(或构造方法、constructor)的使用
- 2.1.10 JavaBean
- 2.1.11 包装类(Wrapper)
- 2.1.12 类的成员之四:代码块(初始化块)
- 2.1.13 类的内部成员之五:内部类
- 2.2 面向对象的三大特征:封装性、继承性、多态性
- 2.2.1 封装性
- 2.2.2 继承性 (判断继承性:a is b是否成立)
- 2.2.3 Object类
- 2.2.4 多态性 (一个事物的多种形态)
- 2.3 其他关键字
- 2.3.1 this的使用
- 2.3.2 package
- 2.3.3 super
- 2.3.4 instanceof (父类向下转型成子类,即强制转)
- 2.3.5 static
- 2.3.6 final:用来修饰类、方法、变量
- 2.3.7 抽象类和抽象方法 abstract
- 2.3.8 接口interface (和类是并列关系)(=多重继承)
- 三、异常
- 1、异常概述
- 2、常见异常
- 3、异常处理机制一:try-catch-finally(出现异常,自己解决掉了)
- 4、异常处理机制二:throws+异常类型
- 5、手动抛出异常
- 6、用户自定义异常类
- 7、异常练习
- 四、设计模式
- 五、Java并发
一、基础知识
- 内存中的信息断电后会消失
- cpu从内存里取指令,然后执行指令
- 硬盘中的数据要先加载到内存才能被cpu读取,cpu向内存中读写数据
- 每个字节在内存中都有一个唯一的地址
- 网卡:将计算机接入局域网LAN(学校)的设备
- 各种客户端(pc/ios/ipad…)和服务器端通过互联网相连,互联网>因特网>万维网
- 第一阶段:javase ---- java基础
mysql
jdbc
第二阶段:javaweb-- html、css
JavaScript、jQuery
tomcat、xml
http
servlet、服务器端组件
jsp、el表达式、jstl
cookie、session
filter、listener
Ajax
第三阶段:javaEE (框架)— 系统整体分层解耦框架spring ssm
轻量级web框架springmvc
轻量级持久层框架mybatis
服务器操作系统linux
内存数据库redis
项目架构和jar包管理工具maven
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-bmfe7joQ-1645784029144)(C:\Users\13520\AppData\Roaming\Typora\typora-user-images\image-20220207160635972.png)] - 命令行命令 d: //进入d盘
md xxx //创建名为xxx的目录
cd d:\xxx //进入d盘下xxx目录(进入指定目录)
cd… //退出到上一层
dir //列出目录
del //删除
javac 源文件名.java //编译java源文件
java 类名 //运行java类
java在记事本写,保存为.java文件,在命令行编译、运行,则结果显示在命令行 - 后台开发:java、php、python、go、node.js
- java没有c++的指针,没有多重继承(用接口取代了)
- java的跨平台特性,用jvm实现,同一个java程序可以在三个不同的操作系统实现,因为有了jvm(舞台)
- 文档注释(java特有) /** 文档内容可被javadoc解析,生成一套以网页形式体现的该程序的说明文档
*/ (在javadoc生成的index里可以看见) - public类的类名(public class xxx{})必须与源文件(xxx.java)同名
- eclipse快捷键使用:Alt+shift+s 快生成setter、getter、构造器等
ctrl+光标指定 查看源码
ctrl+k 快速查找 - 调用一个类时,若属性或方法是static修饰的,则可直接类名.属性/方法来调用,若没有static修饰,则需new一个对象后,再对象.属性/方法调用
- 方法形参为addCustomer.(Customer cust){ }这种类对象时,实参用addCustomer.(new Customer(""…))调用
- debug:设置断点,然后右键debug as java application
然后step into跳入-----进入当前行所调用的方法中
step over跳过-----执行完当前语句,进入下一行 (上一行被执行完了)
step return跳回—执行完当前行所在的方法,进入下一行
drop to frame------回到当前行所在方法的第一行
resume恢复---------(执行完当前行所在断点的所有代码),进入下一个断点,如果没有就结束
step into失灵:右键debug as Debug Configurations,Alternate JRE要选择jdk不能是jre - 在本类中的mian方法里使用本类中的其他方法,则需要new出一个本类对象
- Java中的JUnit单元测试(需要测试一段代码,则将其封装到单元测试方法中,将代复制到新建的类和方法中)
(1)选中当前工程右键:build path---->add libraries---->JUnit 4---->下一步----->完成
(2)新建java类,进行单元测试
此时的Java类要求:此类是public的,且提供公共的无参的构造器
(3)此类中声明单元测试的方法
此时的单元测试方法:方法权限是public,没有返回值,没有形参
(4)此单元测试方法上需要声明注释:@Test,并在单元测试类中导入:import org.junit.Test
(5)声明好单元测试方法后,就可以在方法体内测试相关的代码
(6)写完代码后,左键双击单元测试方法名,右键run as---->JUnit Test
(7)若执行结果没有任何异常:绿条
若执行结果出现异常:红条 - 打开文件有.setting/.project/.classpath则是eclipse项目
打开文件有.iml/.idea则是intellij idea项目 - 在eclipse中有workspace和project的概念,一个workspace下可以有多个project工程
IDEA中只有project和module的概念,一个project下有多个module
即workspace相当于project,project相当于module(只是相当于,但还是不一样的)
目前主流的项目都是分布式部署,即一个项目下多个module模块 - idea中双击再ctrl+b查看类的源码
- 基本数据类型: 整型:0 浮点型:0.0 字符型:0 布尔型:false
引用数据类型(类、数组、接口):null (new之后为地址值)
二、Java基础
(一)Java基本语法
-
enum枚举 byte整型 interface接口类型
整型:byte(1) short(2) int(4) long(8)
long后面加l或L才能用(没加时则当成int对待)
byte表示范围为-128~127 (byte很容易超过,慎用)
默认double,用float时后面必须加f
整型常量默认为int型,所以byte b=a+1会失败,因为1是int型
浮点型常量默认为double型 -
包名:全小写
类名接口名:单词首字母大写
变量名方法名:第一个单词首字母小写,其他单词首字母均大写
常量名:全大写,下划线连接单词 -
变量的作用域:其定义声明所在的一对{}内
-
html的var变量是弱类型,一个var包含了所有变量类型,而java的不同类型有不同名称,是强类型变量
-
System.out.println(“hello”+c1);
System.out.println(c1);
\n换行符 \t制表符 \uxxxx显示的是unicode编码集的字符
utf-8是一种unicode编码集 -
出现乱码是因为保存的gbk,输出的utf-8不一致,所以出现乱码
-
自动转换:byte、char、short------>int 三种类型互相运算时结果都为int类型
强制转换(大转小): double a;
int b=(int) a; //损失精度
int c=(int)(Math.random()*90+10); //前后都有括号 -
String s1="学号为"; //string直接创建 "学号为"放在公共池里 String str="123"; 没有s1[0]或str[1]等这种写法 String s2=new String("String对象创建"); //String类的对象s2的"String对象创建"放在堆里 char c1='a' //a=97 char c2='' //错误,char类型里面不能什么都不放,而string类型可以 String ss="学号为"+"1001"; //这里+表示连接,出现一个string即表示连接 int a=1001; String ss="学号为"+a; //输出结果为:学号为1001 System.out.println(c1+a+s1); //输出结果为:1098学号为 +表示连接后则前后整个为string类型 System.out.println(c1+s1+a); //输出结果为:a学号为1001 前后只要有一个string则+表示连接 System.out.println('#'+"\t"+'#'); //输出结果为:# # /char加string还得string int num=Integer.parseInt(str); //输出123 ss.equals("学号为1001"); //结果为true或false
-
二进制:0b或0B开头 int num1=0b110; //输出结果为6
八进制:0开头 int num2=0127; //输出结果为82
十六进制:0x或0X开头 int num3=0x110A; //输出结果为4362 -
比较运算符----instanceof 检查是否是类的对象
“hello” instanceof String 结果true/false
逻辑运算符----逻辑与& (位操作符)
短路与&& 短路或|| (第一个表达式为false时则不再计算第二个)
异或^ (相同为0不同为1)
开发中常用&&和||
位运算符----- 左移<< (即*2)
(都是整数) 右移>> (最高位为0则移出来的空位都补0,最高位为1则移出来的空位都补1)
(都是补码) 无符号右移>>> (不管原来的数是正数还是负数,空出来的都补0)
没有无符号左移<<<
&与运算 |或运算 ^异或运算 ~取反预算(所有位都取反,包括符号位)
三元运算符---- (m>n)?表达式1:表达式2; -
流程控制:if-else for循环 switch
-
使用Scanner类:
import java.util.Scanner;Scanner scan=new Scanner(System.in); int num=scan.nextInt(); String s1=scan.next();String s2=scan.nextLine();char c=s1.charAt(0); //获取s1的第一个字符没有获取char类型的,直接用String类型即可
-
取[a,b]中的任何一个值 (int)(Math.random()*(b-a+1)+a)
Math.random()可以随机产生[0.0,1.0)中的一个double类型数字 -
switch(表达式/变量) 找到的case是个入口,如果没有break,则继续执行下面的每一个case
-
数组: (数组new之后才能引用,否则会报空指针错误)
int数组new之后获得一个地址数组的起始地址,数组名和外层元素初始化值为地址值,且每个元素初始化默认值为0 String数组new之后获得一个地址数组的起始地址,数组名和外层元素初始化值为地址值,且每个元素初始化默认值为null int[] a=new int[100]; //动态初始化 int[] b=new int[]{1,2,3}; //静态初始化 int[][] c=new int[][]{{1,2,3},{4,5},{6}}; //静态初始化 c[0]输出的是第一行元素的起始地址 String[][] s1=new String[3][2]; //动态初始化,若调用s[1][0],则输出null String[][] s2=new String[3][]; //也对,但光有列就不对了,若调用s[1][0],则报错为空指针 s2[1]=new String[4]; //s2的第二个字符串有4个字符 s[x]默认为一个字符串的第x+1个字符 string数组的定义有三种:(1)String arr[] = new String[10]; //创建一个长度为10的String 类型数组。 (2)String arr[] = {"张三","李四"}; (3)String[] str = new String[]{"","","","",""}; String[][] s5= {{"a","bb","ccc"},{"ddddd"},{null}}; 排序:Arrays.sort(a); 长度:string.length()用于求一个字符串中字符的个数,即string类型string.length用于求String数组中一共有几个字符串,即string数组当string[1].length()时也是求这个字符串中一共有几个字符a.length //=100s1.length //=3s1[1].length //=s1数组第二个字符串的长度 数组的赋值:int[] a=new int[10];int[] b=new int[10];b=a; //即数组a的地址值赋给了数组b,数组a和b指向同一起始地址,两个数组元素同时可以互相改,实时接收 数组的复制:通过for循环一个个的赋值,则互相不影响 杨辉三角
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-IK7Khpm1-1645784029149)(C:\Users\13520\Desktop\捕获.JPG)]
二维数组在内存中的存放位置 -
排序 (10个内部排序)
选择排序:直接选择排序、堆排序
交换排序:冒泡排序、快速排序
插入排序:直接插入排序、折半插入排序、希尔排序
归并排序
基数排序
通式排序快速排序:一个哨兵在第一位不动,然后两个low、high指针,从high开始比较,一直到high比
哨兵小了,则换low开始与哨兵比较,一直到low比哨兵大了,则low和high所指元素交
换位置,low和high指针不动,再从high开始下一次比较。
一直换到low的位置>=high的位置,使哨兵交换到low或者high的位置,哨兵的位置即
为最终位置,且左边都比哨兵大,右边都比哨兵小,本趟结束。开始下一趟,下一趟是
将最终哨兵位置的左右分为两部分,分别各自进行快排,哨兵依然为每部分第一个元素
直到最终全部结束
假设:[49 38 65 97 76 13 27 49’]
第1趟:[27 38 13] 49 [76 97 65 49’]
第2趟:[[13] 27 [38]] 49 [[49’ 65] 76 [97]]
第3趟:[[13] 27 [38]] 49 [[49’ [65]] 76 [97]]
最终: 13 27 38 49 49’ 65 76 97冒泡排序:for(int i=0;i<arr.length-1;i++){for(int j=0;j<arr.length-1-i;j++){if(arr[j]>arr[j+1]){int temp=arr[j];arr[j]=arr[j+1];arr[j+1]=temp;}}}
-
java.util.Arrays //Arrays类
boolean equals(int[] a,int[] b) //判断数组是否相等
String toString(int[] a) //输出数组信息 (不用for循环一个个输出了,整个数组一起输出)
void fill(int[] a,int val) //将指定值填充到(整个)数组之中
void sort(int[] a) //对数组排序
int binarySearch(int []a,int key) //堆排序后的数组进行二分法搜索指定值 -
自定义数组的工具类
-
同一个包中,类之间的调用,可随意调;不同包之间,用import来导入包 再调用类
如果是静态static方法,直接类名.方法名即可
如果是非静态方法,则需new一个对象出来,然后用对象.方法名调用 -
一个java文件中,只能有一个public类,剩下的都是别的类,有且只能有一个main方法,是该文件的入口
一个包中,可以有无数个java文件 -
char c1 = ‘A’
char c2 = 65
c1==c2 是ture的 -
理解main方法的语法
由于 Java 虚拟机需要调用类的 main()方法,所以该方法的访问权限必须是 public,又因为 Java 虚拟机在执行 main()方法时不必创建对象,所以该方法必须是 static 的,该方法接收一个 String 类型的数组参数,该数组中保存执行 Java 命令时传递给所运行的类的参数
又因为 main() 方法是静态的,我们不能直接访问该类中的非静态成员,必须创建该类的一个实例对象后,才能通过这个对象去访问类中的非静态成员,这种情况,我们在之前的例子中多次碰到
(二)面向对象OOP
面向过程:以一个个函数为最小单位,考虑怎么做,强调功能行为
面向对象:以一个个类/对象为最小单位,考虑谁来做,强调具备这些功能的对象
把大象装进冰箱:过程----开门、装、关门
对象----人{冰箱.开开、大象.进入、冰箱.关上}、冰箱{开开、关上}、大象{进入}
对象即实例(instance)
2.1 java类及类的成员
2.1.1 java类及类的成员:属性、方法、构造器、代码块、内部类
属性=成员变量=域、字段=field
方法=函数
public class PersonTest {public static void main(String[] args) {Person p1=new Person();//调用属性p1.name="xy"; p1.isMarried=false;//调用方法p1.walk();System.out.println(p1.display()); //输出xy flasep1.talk("chinese");}
}
class Person{//属性(成员变量)String name;int age=1; //不可int age; age=1;这么写,因为相当于将age又操作了,属于代码块里的内容boolean isMarried;//构造器public Person(){}public Person(String n,boolean im){name=n;isMarried=im;}//方法(函数)public void walk() {System.out.println("人走路...");}public String display() {return "名字是:"+name+" 是否已婚"+isMarried; //这里的name是Person类的对象赋值的name}public void talk(String language){System.out.println(language);}//代码块(初始化块){name="xxxxx";isMarried=true;}//内部类class pet{String name;float weight;}
2.1.2 JVM虚拟机
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-oeLejjHU-1645784029151)(C:\Users\13520\AppData\Roaming\Typora\typora-user-images\1111111)]
程序计数器:可以看做是当前线程所执行的字节码的行号指示器.在JVM的概念模型里,字节码解释器工作时
就是通过改变这个计数器的值来选取下一条需要执行的字节码指令。
每条线程都有一个独立的程序计数器,所以程序计数器是线程私有的内存区域。
如果线程执行的是一个Java方法,计数器记录的是正在执行的虚拟机字节码指令的地址;如果线
程执行的是一个Native方法,计数器的值为空。
Java虚拟机规范中唯一一个没有规定任何OutOfMemoryError情况的区域。
Java虚拟机栈:描述Java方法执行的内存模型,每个方法执行的同时会创建一个栈帧,栈帧用于存储局部
变量 (通常所说的栈) 表、操作数栈、动态链接、方法出口等信息。每个方法从调用直至
执行完成的过程,就对应着一个栈帧在虚拟机栈中入栈到出栈的过程**
如果线程请求的栈深度大于虚拟机所允许的深度,将抛出StackOverflowError 异常;
如果虚拟机栈可以动态扩展如果扩展时无法申请到足够的内存就抛出OutOfMemoryError异常;
本地方法栈:本地方法栈与虚拟机栈的区别:虚拟机栈为虚拟机执行Java方法服务(也就是字节码),而本地
方法栈为虚拟机使用到的Native方法服务
Java虚拟机规范对这个区域规定两种异常情况:StackOverflowError 和 OutOfMemoryError异常。
Java堆:Java堆是被所有的线程共享的一块内存区域,在虚拟机启动时创建。Java***堆的唯一目的就是存放***
对象(heap) 实例,(包括实例里的属性)几乎所有的对象实例都在这里分配内存
Java堆是垃圾回收器管理的主要区域,从内存回收的角度看,由于现在收集器基本都采用分代收集
算法,所以Java堆可以细分为:新生代、老生代;从内存分配的角度看,线程共享的Java堆可能划分出
多个线程私有的分配缓冲区(TLAB)。
Java堆可以处于物理上不连续的内存空间中,只要逻辑上是连续的即可。
Java虚拟机规范规定,如果在堆上没有内存完成实例分配,并且堆上也无法再扩展时,将会抛出
OutOfMemoryError异常。
Java堆内存的OOM异常:
内存泄露:指程序中一些对象不会被GC所回收,它始终占用内存,即被分配的对象引用链可达但已无用。
内存溢出:程序运行过程中无法申请到足够的内存而导致的一种错误.内存溢出通常发生于OLD段或Perm段
垃圾收后,仍然无内存空间容纳新的Java对象的情况。
方法区:被所有的线程共享的一块内存区域。它用于**存储已被虚拟机加载的类信息、常量、静态变量、即时
编译*器编译后的代码***等数据。
不需要连续的内存和可以选择固定大小或者可扩展之外, 还可以选择不实现垃圾回收。
Java虚拟机规范规定,当方法区无法满足内存分配的需求时,将抛出OutOfMemoryError异常
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Wy4sxuAi-1645784029170)(C:\Users\13520\AppData\Roaming\Typora\typora-user-images\image-20220129203959619.png)]
栈里存放对象实例的起始地址、局部变量(在每一次使用完后就出栈)、形参
堆里存放对象实例,即对象实例里面的每一个属性(不包括方法)、new出来的结构:对象、数组
方法区:类的加载信息、静态域、常量池
方法区和堆:1个进程1份
虚拟机栈和程序计数器pc:1个线程1份
多个线程共享1个进程当中的方法和堆 [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-qHDpxQ4s-1645784029172)(C:\Users\13520\AppData\Roaming\Typora\typora-user-images\image-20220130201038659.png)]
2.1.3 属性(成员变量)和局部变量
属性分为实例变量(不以static修饰)和类变量(以static修饰)
相同点:先声明后使用、变量都有其对应的作用域
不同点:在类中声明的位置不同
--------属性:直接定义在类的一对{}内
--------局部变量:声明在方法内、方法形参、代码块内、构造器形参、构造器内部的变量
关于权限修饰符的不同
--------属性:可以在声明属性时,指明其权限,使用权限修饰符
--------常用的权限修饰符:private、public、缺省、protected(只有4种)------->封装性
局部变量不可以用权限修饰符
默认初始化值的情况
--------- 属性:类的属性,根据其类型,都有默认初始值
整型:0 浮点型:0.0 字符型:0 布尔型:false
引用数据类型(类、数组、接口):null (new之后为地址值)
(1)方法使用时,可以调用当前类的属性或方法 (方法中还可调用当前类的所有方法)
特殊的,方法A 中又调用了方法A:递归方法
(2)三步:创建类、类的实例化、调用对象的结构–“对象.属性”“对象.方法”
2.1.4 匿名对象
只有new没有将其赋给一个类的对象 没有Person p=new Person();这一步
new Person().walk();
new Person().eat();
匿名对象只能使用一次,其属性数据那些用完一次就没了
使用一般为mall.show(new Phone()); //匿名对象作为形参
2.1.5 重载overload (同一类中)
重载:同一个类中,允许存在一个以上的同名方法,只要参数个数或者参数类型不同即可
方法重载特点:只看参数列表,和返回值类型无关,返回值类型不同不一定是重载,
(重载只看形参)
2.1.6 重写override (子类继承父类)
重写:在子类中根据需要对发从父类中继承来的方法进行改造,也称为方法的重置、覆盖
在程序执行时,子类的方法将覆盖父类的方法
重写的要求:
(1)重写的方法必须具有相同的方法名称、参数列表 不是int double这种<=
(2)子类重写的方法的返回值类型要<=父类被重写的方法的返回值类型(<=即子类的意思)
(3)子类重写的方法的访问权限要>=父类被重写的方法的访问权限
(4)子类不能重写父类中private的方法
(5)子类其他方法抛出的异常要<=父类被重写方法的异常
注意:子类与父类中同名同参数的方法必须同时声明为非static的(即为重写),或者同时
声明为static的(不是重写),因为static方法是属于类的,子类无法覆盖父类的方法
重写即重新写子类中从父类中继承来的不满意的方法
2.1.7 可变个数的形参 (代替数组形参 )
(个数可以为0、1、2、3…) …等于数组[],在程序里被认为是一样的
public void test1(String s) {}
public void test1(String…str) {} //当形参只有一个时,只调用第一个test1,当形参个数为其他时,调用第二个
test.show(“hello”); // test.show(“hello”,“hhhhhhh”,“qqq”);和只有"hello"的输出结果一样
public void show(String**…**strs){ } //可变个数的形参指的是test.show(“hello”);里的"hello"有多少个都可以
public void test1(String…str,int i) {} 不能这么写,因为可变形参必须放到最后
可变形参最多只能有一个
方法的参数传递方式只有一种:值传递 (形参怎么变都不影响实参,除了形参为类对象名、数组名)
值传递:参数是基本数据类型,则赋值给形参的是实参真实存储的数据值
参数是引用数据类型,则赋值给形参的是实参存储数据的地址值
2.1.8 private的使用
private只能在本类中使用(调用或者赋值),不能在本类之外的任何地方使用,包括对象.属性这种
也不能在子类(subclass)中使用
2.1.9 构造器(或构造方法、constructor)的使用
(1)构造器作用:创建对象 Person p=new Person(); //创建类的对象:new+构造器
如果没有显示的定义类的构造器的话,则系统默认提供一个空的构造器
(2)定义构造器格式:权限修饰符 类名(形参列表) {}
public Person(){}
public Person(String n,boolean im){
name=n;
isMarried=im;
}
构造器可以定义多个(构成重载,根据形参不同来区分)
(3)一旦显示的定义了类的构造器之后,系统就不再提供默认的空参构造器
(4)一个类中,至少会有一个构造器(至少一个默认)
(5)只在new出对象时使用这一次
2.1.10 JavaBean
JavaBean是一种用Java语言写成的可重用组件
所谓JavaBean是指符合以下标准的Java类:(1)类是公共的
(2)有一个无参的公共的构造器
(3)有属性,且有对应的get、set方法
JavaBean使对象可以随时复制粘贴而不用关心任何改变,用于在数据库、内部jsp页面、servlet等各种操作
package com.xueyu.test;
public class Customer {private String firstName;private String lastName;private int account;public Customer(String f,String l) {this.firstName = f;this.lastName = l;}
}package com.xueyu.test;
public class Bank {private Customer[] customers; //存放客户的数组private int numberOfCustomer; //客户个数public Bank() {}//添加客户,每次调用这个方法就增加一个客户public void addCustomer(String f,String l) {Customer cust = new Customer(f,l);customers[numberOfCustomer] = cust;numberOfCustomer++;}
}
2.1.11 包装类(Wrapper)
针对8种基本数据类型定义相应的引用类型—包装类(封装类),使得基本数据类型的变量具有类的特征
有了类的特点,就可以调用类的方法,Java才是真正的面向对象
基本数据类型 包装类(父类Number)
byte Byte
short Short
int Integer
long Long
float Float
double Double
boolean Boolean
char Character
基本数据类型: 整型:0 浮点型:0.0 字符型:0 布尔型:false
引用数据类型(类、数组、接口):null (new之后为地址值)
基本类型、包装类和String类间的转换
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-aQx0PIvp-1645784029174)(C:\Users\13520\AppData\Roaming\Typora\typora-user-images\image-20220207160610151.png)]
装箱:Integer in1 = new Integer(a)
Integer in1 = new Integer(“123”) //或者(123)也可以,但不可以是字母
Float fl1 = new Float(“12.3”) //或者(12.3f)
拆箱:int i1 = in1.intValue(); //包装类变成基本数据类型,方便数据加减乘除
float f1 = fl1.floatValue();
JDK 5.0新特性:自动装箱与自动拆箱
自动装箱:int num1 = 10;
Integer in1 = num1; //本来要new一个Integer类,但num1自动装箱
boolean b1 = true;
Boolean b2 = b1; //不用再new,直接=,更加方便了
自动拆箱:int num2 = in1; //和自动装箱一样,直接写,不用再调用floatValue()方法
基本数据类型、包装类------>String类
(1)连接运算 String str1 = num1 + " "
(2)调用包装类的valueOf()方法 String str2 = String.valueOf(num1)
String类------>基本数据类型、包装类 (不可强转)
(1)调用包装类的parseXxx() int num2 = Integer.parseInt(str1);
面试题:
(1)Object o1 = true ? new Integer(1) : new Double(2.0);
System.out.println(o1); //输出1.0,因为编译的时候要求前后统一了
(2)Integer m=1;
Integer n=1;
System.out.println(m == n); //输出true,因为Integer类里的IntegerCache(缓存)方法定义了Integer[]
数组,保存了从-128到127的整数,如果使用自动装箱的方式,给Integer赋
值范围在-128到127范围内时,可以直接使用数组中的元素,不用再去new
Integer x=128;
Integer y=128;
System.out.println(x == y); //输出false ,因为相当与再去new了
(3)背景:输入学生成绩,给vector添加数组,数组元素为Object类型
for(;😉{
System.err.println(“请输入学生成绩:”);
int score = Scanner.nextInt();
// Integer inScore = new Integer(score); //将int类型的成绩转换为包装类
// v.addElement(inScore); //用多态,形参是Object类,实参是Integer包装类
v.addElement(score); //直接自动装箱即可
}
//数组中每个元素与最大值比较,需要拆箱
Object obj = v.elementAt(i);
Integer inScore = (Integer)obj; //子类…=父类… 强转
int score = inScore.intValue(); //调用方法拆箱
2.1.12 类的成员之四:代码块(初始化块)
{ static{
… //类内部用{}的就是代码块 …
} }
(1)代码块的作用:用来初始化类、对象的
代码块如果有修饰的话,只能使用 static,故分为静态代码块 vs 非静态代码块
(2)静态代码块:内部可以有输出语句
随着类的加载而执行,而且只执行一次 (static属性/方法是随着类的加载而加载)
作用:初始化类的信息
如果一个类中,定义了多个静态代码块,则按照声明的先后顺序执行
静态代码块的执行,优先于非静态代码块的执行
静态代码块内只能调用静态的属性、静态的方法,不能调用非静态的结构
由父类到子类,静态先行,在同一个java文件中,不同类的static静态都先行
(3)非静态代码块:内部可以有输出语句
随着对象的创建而执行
每创建一个对象,就执行一次非静态代码块
作用:可以在创建对象时,对对象的属性等进行初始化
如果一个类中,定义了多个非静态代码块,则按照声明的先后顺序执行
非静态代码块内可以调用静态的属性、静态的方法,或非静态的属性、非静态的方法
(4)对属性可以赋值的位置: ①默认初始化
②显式初始化
③构造器中初始化
④有了对象以后,可以通过"对象.属性"或"对象.方法"的方式,进行赋值
⑤在代码块中赋值
//总结:由父类到子类,静态先行(new一个对象后) //输出结果是class Root{ Root 的静态初始化块static{ Mid 的静态初始化块 System.out.println("Root 的静态初始化块"); Leaf 的静态初始化块} Root 的普通初始化块{ Root 的无参数的构造器System.out.println("Root 的普通初始化块"); Mid 的普通初始化块} Mid 的无参数的构造器public Root(){ Mid 的带参数构造器,其参数值:xySystem.out.println("Root 的无参数的构造器"); Leaf 的普通初始化块} Leaf 的构造器}class Mid extends Root{static{System.out.println("Mid 的静态初始化块");}{System.out.println("Mid 的普通初始化块");}public Mid(){super();System.out.println("Mid 的无参数的构造器");}public Mid(String msg){//通过 this 调用同一类中重载的构造器this();System.out.println("Mid 的带参数构造器,其参数值:"+ msg);}}class Leaf extends Mid{static{System.out.println("Leaf 的静态初始化块");}{System.out.println("Leaf 的普通初始化块");} public Leaf(){//通过 super 调用父类中有一个字符串参数的构造器super("xy");System.out.println("Leaf 的构造器");}}public class LeafTest{public static void main(String[] args){new Leaf(); //new Leaf();}}
//如果有main方法里有new,则也是从父类到子类,最后再到main方法执行静态代码
2.1.13 类的内部成员之五:内部类
当一个事物的内部,还有一个部分需要一个完整的结构进行描述,而这个内部的完整的结构又只为外部事物提
供服务,那么整个内部的完整结构最好使用内部类
(1)Java中允许将一个类A声明在另一个类B中,则类A就是内部类,类B就是外部类(A、B名字不能同)
(2)内部类的分类:成员内部类(直接定义在类中)VS 局部内部类(方法内、代码块内、构造器内)
(3)成员内部类:作为外部类的成员-------调用外部类的结构
可以被static修饰
可以被4种不同的权限修饰
作为一个类---------类内可以定义属性、方法、构造器等
可以被final修饰,表示此类不能被继承,即不使用final,就可以被继承
可以abstract修饰
public class InnerClassTest {public static void main(String[] args) { //创建Dog实例(静态的成员内部类)Person.Dog dog = new Person.Dog();dog.show();//创建Bird实例(非静态的成员内部类)// Person.Bird bird = new Person.Bird(); 错误的Person p = new Person();Person.Bird bird = p.new Bird();bird.sing();System.out.println(); bird.display("喜鹊");}}class Person{String name = "李雷";int age;public void eat(){System.out.println("人,吃饭");}//静态成员内部类static class Dog{String name;int age; public void show(){System.out.println("卡拉是条狗");// eat();}}//非静态成员内部类class Bird{String name = "杜鹃";public Bird(){ } public void sing(){System.out.println("我是一只猫头鹰");Person.this.eat();//调用外部类的非静态属性eat();System.out.println(age);}public void display(String name){System.out.println(name); //方法的形参System.out.println(this.name); //内部类的属性System.out.println(Person.this.name); //外部类的属性}}public void method(){//局部内部类(方法内)class AA{}}{ //局部内部类(代码块内)class BB{}}public Person(){//局部内部类(构造器内)class CC{ }}}匿名内部类1.匿名内部类不能定义任何静态成员、方法和类,只能创建匿名内部类的一个实例一个匿名内部类一定是在new的后面,用其隐含实现一个接口或实现一个类 2.格式:new 父类构造器(实参列表)|实现接口(){//匿名内部类的类体部分} 3.匿名内部类的特点匿名内部类必须继承父类或实现接口匿名内部类只能有一个对象匿名内部类对象只能使用多态形式引用interface Product{public double getPrice();public String getName();}public class AnonymousTest{public void test(Product p){System.out.println("购买了一个" + p.getName() + ",花掉了" + p.getPrice());}public static void main(String[] args) {AnonymousTest ta = new AnonymousTest();//调用test方法时,需要传入一个Product参数,//此处传入其匿名实现类的实例ta.test(new Product(){public double getPrice(){return 567.8;}public String getName(){return "AGP显卡";}});}}//在局部内部类的方法中(比如:show)如果调用局部内部类所声明的方法(比如:method)中的局部变量的话,//要求此局部变量声明为final的。// jdk 7及之前版本:要求此局部变量显式的声明为final的// jdk 8及之后的版本:可以省略final的声明public void method(){//局部变量int num = 10; class AA{public void show(){// num = 20; //必须是fina的System.out.println(num);}}}
2.2 面向对象的三大特征:封装性、继承性、多态性
2.2.1 封装性
隐藏对象内部的复杂性,只对外公开简单的接口。该隐藏的隐藏,该暴露的暴露
“高内聚,低耦合”:内部自己完成;仅对外暴露少量的方法用于使用
class Animal{String name;int age;private int legs; //私有属性,只能在本类中被使用,此外任何地方都不能用 //对属性的设置public void setlegs(int l) {if(l>0&&l%2==0) legs=1;else legs=0; //+抛出一个异常} //对属性的获取public int getLegs() {return legs;}}Animal ani=new Animal();
ani.setLegs(4);
Ani_Legs=ani.getLegs();
封装性的体现:(1)将类的属性私有化(private),同时提供公共的(public)方法来获取(getXxx)和设置(setXxx)
此属性的值
(2)不对外暴露的私有的方法,例如排序等
(3)单例模式
封装性的体现,需要权限修饰符来配合,Java四种:private、缺省(什么也不写)、protected、public
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-KwHx6Cap-1645784029176)(C:\Users\13520\AppData\Roaming\Typora\typora-user-images\image-20220202103939265.png)]
同一个工程下都有jar包,所以public可以用任何
class的权限修饰符只可以用public和缺省
2.2.2 继承性 (判断继承性:a is b是否成立)
public class A extends B{
//String name; //B的属性,A继承了,所以A不用写,直接调用
//int n;
int major; //A独有的属性
}
子类A一旦继承父类B之后,子类A就获取了父类B中声明的属性、方法,可以直接A对象.属性调用
父类中private属性的,子类继承后仍然认为获得了父类中该私有的结构,只是因为封装性影响,使得子类
不能直接调用父类的结构
子类继承父类后,还可以声明自己特有的属性或方法
特点:(1)一个**子类(subclass)只能有一个父类,一个父类(superclass)**可以派生出多个父类
(2)A->B->C 则A是C的间接父类(间接继承的),B是C的直接父类(直接继承)
(3)子类获得直接父类和所有简介父类的属性方法
2.2.3 Object类
(1)如果没有显示的声明一个类的父类的话,则此类继承于java.lang.Object类
所有的java类(除Object类本身外),都直接或间接继承于java.lang.Object类
意味着,所有的java类具有Object类声明的功能
Object类是所有Java类的根父类
method(Object obj){…}可以接受任何类作为其参数
Object类只声明了一个空参构造器
Object的各种方法:equals()、toString()、getClass()、hashCode()、clone()、finalize()、
wait()、notify()、notifyAll()
(2)Object类中的主要结构
1)public Object()
2)public boolean equals(Object obj) :对象比较(只适用于引用类型)
①==和equals的u区别:既可以比较基本类型也可以比较引用类型,对于基本类型则比较值,
引用类型则比较内存地址
equals是属于java.lang.Object类里面的方法,如果该方法没有被重写过
则默认也是(即比较内存地址)
我们可以看到String等类的equals方法是被重写过的,而且String类在
日常开发中用的比较多,久而久之,形成了equals是值比较的错误观点
具体要看自定义类里有没有重写Object的equals方法来判断
像String、Date、File、包装类等都重写了Object类中的equals方法,重
写以后,比较的不是内存地址是否相同,而是两个对象的实体内容是否相同
通常情况下,重写equals方法会比较类中的相应属性是否都相等
==是运算符,equals是方法
==适用于基本类型和引用类型,equals只适用于引用类型
②自定义类如果使用equals()的话,也通常是比较两个对象的实体内容是否相同,即需要对其重写
实际开发中,通常使用自动调用的equals(),不用自己写
//重写本类Customer类的equals方法//即比较两个对象的实体内容(类的属性)是否相同public boolean equals(Object obj) {if(this == obj) { //若两个对象的内存地址相同,则指向同一实体,必同return true;}if(obj instanceof Customer) { //这里假设本类为Customer类Customer cust = (Customer)obj;return this.age == cust.age && this.name.equals(cust.name);}else {return false;}}
3)public int hashCode():获得Hash码
4)public String toString():对象打印时调用
①返回类名和它的引用地址(当我们输出一个对象名时,实际上调用了当前对象的toString方法)
System.out.println(p1); //如果输出结果不是类名和引用地址,则该类重写了toString方法
②在进行String与其他类型数据的连接操作时自动调用toString()方法
Person p1 = new Person();
System.out.println(“test=”+p1);
System.out.println(“test=”+p1.toString());
③可在自定义类中重写toString()方法
像String、Date、File、包装类等都重写了Object类中的toString方法,使其返回“实体内容”信息
④基本数据类型转换为String类型时,调用了对应包装类的toString()方法
int a=10;
System.out.println(“a=”+a);
2.2.4 多态性 (一个事物的多种形态)
(1)对象的多态性:父类的引用指向子类的对象 Person p2=new Man(); //Man是Person的子类
(2)多态的使用:当调用子父类同名同参数的方法时,实际执行的是子类重写父类的方法----虚拟方法调用
编译期只能调用父类中声明的方法,但运行期实际执行的是子类重写父类的方法
编译看左边,运行看右边
(3)Person p2=new Man(); p2的范围是Person类,不能调用子类特有的,调用的只能是父类的属性方法
但执行时执行的是子类中被重写的方法
(4)多态性的使用前提:类的继承
方法重写(若子类不重写,则没必要使用多态性)
(5)
public class animalTest {public static void main(String[] args) {animalTest test =new animalTest();test.func(new Dog()); //多态性的用法test.func(new Cat());}//有多态性,不需要重载方法 //没有多态性,需要重载很多方法public void func(Animal animal) { public void func(Dog dog) { animal.eat(); dog.eat();animal.shout(); dog.shout();} }} public void func(Cat cat) {class Animal{ cat.eat();public void eat() { cat.shout();System.out.println("动物吃"); }}public void shout() {System.out.println("动物叫");}}class Dog extends Animal{public void eat() {System.out.println("狗吃");}public void shout() {System.out.println("狗叫");}}class Cat extends Animal{public void eat() {System.out.println("猫吃");}public void shout() {System.out.println("猫叫");}}
(6)对象的多态性,只适用于方法,不适用于属性,即属性的编译和运行都看左边,都是父类的属性
(7)虚拟方法调用:多态情况下,将父类中被重写的方法称为虚拟方法,父类根据赋予它的不同子类对象,
动态调用属于子类的该方法,因此这样的方法编译器是无法确定的,运行时才能确定
有了对象的多态性后 ,在编译器只能调用父类中声明的方法,但运行期实际执行子类重写父类的方法
(8)父类----->子类 向下转型:使用instanceof进行判断 //强制转,先用多态,才能判断ture n是int类型
子类----->父类 向上转型:多态 (常用) //自动转Animal a=new Dog(); double a=n;
为何使用向下转型:有了对象的多态性后,内存中实际上是加载了子类特有的属性和方法的,但由于
变量声明为父类类型,导致编译时只能调用父类中声明的属性和方法,子类特有的属性和方法不能调
用,如何才能调用子类特有的属性和方法?使用向下转型
(9)多态是编译行为还是运行时行为? 运行时行为(编译看父类,但执行子类中该重写的方法)
(10)谈谈你对多态性的理解
①实现代码的通用性 Object类中定义的public boolean equals(Object obj){} 其中的obj可以接收任何类
②JDBC:使用java程序操作(获取数据库连接、crud)数据库,写的参数都是父类,但实际调用有很多子类,
因为有很多种数据库,mysql、oracle、db2、sql server之类的
③抽象类、接口的使用肯定体现了多态性,因为抽象类、接口不能实例
2.3 其他关键字
this、package、import、super、instanceof、static、final、abstract、interface
2.3.1 this的使用
(1)this表示当前对象,可以调用类的属性、方法和构造器
this在方法内部使用,即这个方法所属对象的引用 (方法内部使用this.方法()来调用当前内部的方法)
this在构造器内部使用,表示该构造器正在初始化的对象
当在方法内需要用到调用该方法的对象时,就用this
可以用this来区分属性和局部变量 this.name(属性)=name(局部变量)
(2)通常情况下都选择省略“this.”,只有当方法的形参和类的属性同名时,必须显式的使用“this.变量”
(3)this修饰、调用构造器
class Person{......public Person(){this.eat(); //Person初始化时,必须需要的40行代码......}public Person(int age){this(); //调用了空参的构造器Person(),执行了必须要的40行代码this.age=age;}public Person(String name,int age){this(age); //根据this形参的不同来调用不同的构造器this.name=name;}}不能通过this.(形参)方式调用自己一个类中有n个构造器,则最多有n-1个构造器中使用了this(形参)一个构造器中,最多只能用一个this(形参)调用
2.3.2 package
包名中,com.xueyu.test 每点一次,就代表一层文件目录
同一个包下,不可命名同名的接口、类,不同包下可以
如果需要的类或接口是java.lang下的,则不需要import,直接使用,因为是核心类,系统自动引入
如果在源文件中,使用了不同包下的同名的类,则必须至少有一个类需要以全类名的方式显示
jar包里都需要import
import java.lang.*可以导入所有lang的包,但不能导入lang的包的子包,即不可用java.lang.x.x
import static:导入指定类或接口中的静态结构:属性或方法
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-QGhWGlty-1645784029176)(C:\Users\13520\AppData\Roaming\Typora\typora-user-images\image-20220202130211999.png)]
2.3.3 super
super:使用父类中被重写的方法
super.属性 //该属性在子类和父类中都有定义,用this.属性区分子类,super.属性区分父类
在子类中,可使用super.属性/方法来显示的调用父类中声明的属性方法,但一般情况都省略super
若属性在子类和父类中都有定义,则使用父类的时,用super.属性区分
super调用构造器,和this() this(age)一样,只不过是在子类构造器中用了父类的构造器super()等
**在类的一个构造器中,针对于this(形参列表)【本类中重载的构造器】或super(形参列表)【父类中重写的构造器】**只能二选一,不能同时出现
super(形参列表)必须声明在子类构造器的首行
在子类构造器的首行如果super或this都没有,则默认调用的是父类中空参的构造器,所以子类和
父类必须有构造器
子类中,必须至少有一个构造器使用了super
super.属性用于子类父类中都声明了相同的属性时
super.方法()用于在子类的方法中要使用父类中那个被子类重写的父类的方法
2.3.4 instanceof (父类向下转型成子类,即强制转)
(1)Dog d=(Dog)animal,使用强转时,可能出现ClassCastException异常,所以引入了instanceof关键字
(2)使用:a instanceof A----判断对象a是否是类A的实例,如果是返回ture,否则返回false
(3)使用情景:为了避免在向下转型时出现异常,先进行instanceof的判断,若返回true就进行向下转型
Animal animal=new Dog(); //向上转型,即多态 父类…=子类…
if(animal instanceof Dog){
Dog dog2=(Dog)animal; //向下转型,即强制转 子类…=父类…
}
2.3.5 static
编写一个类时没有产生实质上的对象,只有new之后才会产生出对象,这时系统才会分配内存空间
给new出来的对象,都某些特定的数据在内存空间中只有一份,无论new了多少个对象,这些对象都
共享这个属性/方法/代码块/内部类**(构造器不行)**
使用static修饰变量 :c1.nation=“111”;
c2.nation=“222”
输出c2的nation,结果是111,因为nation是static修饰的,只有一个内存空间,当某
一个对象修改静态变量时,会导致其他对象调用此静态变量时,是修改过了的
静态变量(类变量)随着类的加载而加载,所以可以直接类名.属性来调用静态变量
静态变量的加载要早于对象的创建
由于类只会加载一次,则静态变量在内存中也只会存在一份:存在方法区的静态域中
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-pDRmATQE-1645784029177)(C:\Users\13520\AppData\Roaming\Typora\typora-user-images\image-20220208124143340.png)]
使用static修饰方法:随着类的加载而加载,通过“类名.静态方法”的方式进行调用 (晚出生的不能调早出生的)
静态方法中只能调用静态的方法/属性,不能使用this、super关键字,因为没对象
非静态方法中既可以调用非静态的方法/属性,可以调用静态的方法/属性,调用静态时
前面省略的是类名,而不是this
开发中如何确定一个属性是否要声明为static:属性是可以被多个对象所共享的,不会随着对象的不同而不同
开发中如何确定一个方法是否要声明为static:操作静态属性的方法,通常设置为static
工具类中的方法,习惯上声明为static,像Math、Arrays等
2.3.6 final:用来修饰类、方法、变量
用来修饰的这个类不能被其他类继承(String类、System类、StringBuffer类)不能作为父类
用来修饰的方法不能被子类重写 不能被重写
用来修饰的变量,此时变成了一个常量,名称大写,且只能被赋值一次
(1)final修饰属性,可以赋值的位置有:显式初始化、代码块中初始化、构造器中初始化
(2)用来修饰局部变量即形参时,表明形参是一个常量,一旦赋值后,只能在方法体内使用,
且不能在方法体内再被重新赋值
static final:用来修饰全局变量
2.3.7 抽象类和抽象方法 abstract
抽象类:类变得越来越具体,而父类变得越来越一般,更通用。类的设计应该保证父类和子类能够共享特征
有时将一个父类设计得非常抽象,以至于它没有具体的实例,这样的类叫做抽象类
abstract:用来修饰类、方法
抽象类:不能实例化,但可以被继承
抽象类中一定有构造器,便于子类实例化时调用
开发中,都会提供抽象类的子类,让子类对象实例化,实现相关的操作
抽象方法:抽象方法只有声明,没有方法体,即public abstract void eat(); 方法无{},以;结尾
包含抽象方法的类,一定是一个抽象类,不能实例化。反之,抽象类中可以没有抽象方法
若子类重写了父类中所有的抽象方法,此子类方可实例化
若子类没有重写父类中所有的抽象方法,则此子类也是一个抽象类,需要abstract修饰
abstract不能用来修饰变量、代码块、构造器
abstract不能用来修饰私有方法、静态方法、final 的方法、final 的类
抽象类应用:抽象类是用来模型化那些父类无法确定全部实现,而是由其子类提供具体实现的对象的类/* Java 允许类设计者指定:超类声明一个方法但不提供实现,该方法的实现由子类提 供。这样的方法称为抽象方法。有一个或更多抽象方法的类称为抽象类。
-
Vehicle是一个抽象类,有两个抽象方法。注意:抽象类不能实例化 new Vihicle()是非法的public abstract class Vehicle{public abstract double calcFuelEfficiency();//计算燃料效率的抽象方法public abstract double calcTripDistance();//计算行驶距离的抽象方法}public class Truck extends Vehicle{public double calcFuelEfficiency(){ //写出计算卡车的燃料效率的具体方法}public double calcTripDistance(){ //写出计算卡车行驶距离的具体方法}}public class RiverBarge extends Vehicle{public double calcFuelEfficiency() { //写出计算驳船的燃料效率的具体方法}public double calcTripDistance( ) { //写出计算驳船行驶距离的具体方法}}
(1)为什么抽象类不能使用final,因为抽象类是用来被继承的,而final不能继承
(2)抽象类的匿名子类
public class PersonTest {public static void main(String[] args) { method(new Student()); //匿名对象 只知道类,不知道对象 Worker worker = new Worker(); method1(worker); //非匿名的类非匿名的对象 正常写法 method1(new Worker()); //非匿名的类匿名的对象 System.out.println("*********************");//创建了一个匿名子类的对象:p (这一大段都是创建的对象p)Person p = new Person(){ //Person是抽象类,按理说不能被实例化,故p是匿名子类的对象p@Override //代表重写 //认为这是Person类的子类public void eat() {System.out.println("吃东西");}@Overridepublic void breath() {System.out.println("呼吸空气");} };method1(p);System.out.println("**********************"); //创建匿名子类的匿名对象method1(new Person(){@Overridepublic void eat() {System.out.println("吃零食");}@Overridepublic void breath() {System.out.println("云南的空气");} });} public static void method1(Person p){p.eat();p.walk();} public static void method(Student s){}
}
class Worker extends Person{ @Overridepublic void eat() {}@Overridepublic void breath() {}
}public class Num {}
abstract class Creature{public abstract void breath();
}
abstract class Person extends Creature{String name;int age; public Person(){}public Person(String name,int age){this.name = name;this.age = age;}//不是抽象方法
// public void eat(){
// System.out.println("人吃饭");
// }//抽象方法public abstract void eat();public void walk(){System.out.println("人走路");}
}
class Student extends Person{public Student(String name,int age){super(name,age);}public Student(){}public void eat(){System.out.println("学生应该多吃有营养的。");}@Overridepublic void breath() {System.out.println("学生应该呼吸新鲜的无雾霾空气");}
}
(3)多态的应用:模板方法设计模式
抽象类体现的就是一种模板模式的设计,抽象类作为多个子类的通用模板,子类在抽象类的基础上进行扩
展、改造,但子类总体上会保留抽象类的行为方式当功能内部一部分实现是确定的,一部分实现是不确定
的,这时可以把不确定的部分暴露出去,让子类去实现
换句话说,在软件开发中实现一个算法时,整体步骤很固定、通用,这些步骤已经在父类中写好了。但是某
些部分易变,易变部分可以抽象出来,供不同子类实现,这就是一种模板模式
public class TemplateTest {public static void main(String[] args) { SubTemlate t = new SubTemlate();t.sendTime();}}abstract class Template{//计算某段代码执行所需花费的时间public void sendTime(){long start = System.currentTimeMillis();code(); //不确定部分,易变的部分long end = System.currentTimeMillis();System.out.println("花费的时间为:" + (end - start));}public abstract void code(); //子类中重写了这个方法,所以执行的是重写的}class SubTemlate extends Template{ @Overridepublic void code() {for(int i = 2;i <= 1000;i++){boolean isFlag = true;for(int j = 2;j <= Math.sqrt(i);j++){if(i % j == 0){isFlag = false;break;}}if(isFlag){System.out.println(i);}}}}
2.3.8 接口interface (和类是并列关系)(=多重继承)
(1)有时需要从几个类中派生出一个子类,继承它们所有的属性和方法,有时必须从几个类中抽取出一些共同
的行为特征,但是Java 不支持多重继承,故用接口代替多重继承
接口的主要用途就是被实现类实现(面向接口编程)
(2)接口就是规范,定义的是一组规则,体现了现实世界中“如果你是/要…则必须能…”的思想
继承是一个"是不是"的关系,而接口实现则是"能不能"的关系
接口的本质是契约,标准,规范,就像我们的法律一样,制定好后大家都要遵守
接口本质是一种特殊的抽象类
(3)接口特点:接口是抽象方法和常量值定义的集合
接口中的所有成员变量/属性都默认是由 public static final(全局变量)修饰的
接口中的所有抽象方法都默认是由 public abstract 修饰的
接口中没有构造器,意味着接口不能实例化
接口采用多继承机制
JDK7 及以前:只能定义全局常量和抽象方法
JDK8:除了全局常量和抽象方法之外,还可以定义静态方法、默认方法
接口的具体使用,体现多态性
接口的主要用途就是被实现类实现
(4)接口通过让类去实现(implements)的方式来使用
如果实现类覆盖了接口中的所有方法,则此实现类就可以实例化
如果实现类没有覆盖接口中所有的抽象方法,则此实现类仍为一个抽象类
java类可以实现多个接口
class AA extends BB implementd CC,DD,EE 类AA是接口CC,DD,EE的实现类
接口与接口之间是继承,而且可以多继承
接口可以继承多个接口,但不能继承类
(5)接口在类中使用的话要用多态,因为接口不能实例化,所以用多态
例如接口USB,想要使用USB就得USB usb= new Flash()多态,其中class Flash implements USB
接口和继承区别:类只能继承一个类,但可以实现多个接口
接口里的方法都是抽象方法,所以没有方法体,可有参数
public class InterfaceTest {public static void main(String[] args) {System.out.println(Flayable.MAX_SPEED);System.out.println(Flayable.MIN_SPEED);}}interface Flayable{ //全局变量public static final int MAX_SPEED = 7900; int MIN_SPEED = 1;//省略了 public static final //抽象方法public abstract void fly(); void stop();//省略了 public abstract //接口不能有构造器// public Flayable(){// // } }interface Attackable{void attack();}class Plane implements Flayable{ //Plane类实现了接口Flayable@Overridepublic void fly() {System.out.println("飞机通过引擎起飞"); }@Overridepublic void stop() {System.out.println("驾驶员减速停止");}}abstract class Kite implements Flayable{@Overridepublic void fly() {}}class Bullet extends Object implements Flayable,Attackable,CC{@Overridepublic void attack() {// TODO Auto-generated method stub}@Overridepublic void fly() {// TODO Auto-generated method stub }@Overridepublic void stop() {// TODO Auto-generated method stub }@Overridepublic void method1() {// TODO Auto-generated method stub }@Overridepublic void method2() {// TODO Auto-generated method stub }}interface AA{void method1();}interface BB{void method2();}interface CC extends AA,BB{ }
(6)接口和抽象类的对比
No. | 区别点 | 抽象类 | 接口 |
---|---|---|---|
1 | 定义 | 包含抽象方法的类 | 主要是抽象方法和全局常量的集合 |
2 | 组成 | 构造方法、抽象方法、普通方法、常量、变量 | 常量、抽象方法、(jdk8.0:默认方法、静态方法) |
3 | 使用 | 子类继承抽象类(extends) | 子类实现接口(implements) |
4 | 关系 | 抽象类可以实现多个接口 | 接口不能继承抽象类,但允许继承多个接口 |
5 | 常见设计模式 | 模板方法 | 简单工厂、工厂方法、代理模式 |
6 | 对象 | 都通过对象的多态性产生实例化对象 | |
7 | 局限 | 抽象类有单继承的局限 | 接口没有此局限 |
8 | 实际 | 作为一个模板 | 是作为一个标准或是表示一种能力 |
9 | 选择 | 如果抽象类和接口都可以使用的话,优先使用接口,因为避免单继承的局限 |
(7)[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-xfrNukPo-1645784029179)(C:\Users\13520\AppData\Roaming\Typora\typora-user-images\image-20220212105910859.png)]
若父类和父类的父类同时有x,子类中x的值为父类中x的值2,选择最近的父类,如果父类没有x,父类的父类
有,则子类的x为父类的父类,子类中this.x和super.x都是2
若父类和接口中同时有x,若要使用接口中的x,则直接接口名.x,因为接口里的x是全局变量
(8)Java8中关于接口的改进
Java8中可以为接口添加静态方法和默认方法
从技术角度来说,这是完全合法的,只是它看起来违反了接口作为一个抽象定义的理念
//接口public interface CompareA {//静态方法 使用static关键字修饰,只能通过接口名直接调用静态方法,并执行其方法体public static void method1() {System.out.println("CompareA:西安");}//默认方法 使用default关键字修饰,可以通过实现类对象来调用,我们在已有的接口中提供新方法的public default void method2(){ // 同时,还保持了与旧版本代码的兼容性,比如java8System.out.println("CompareA:深圳");//API中对 Collection、List、Comparator 等接口} //提供了丰富的默认方法default void method3(){System.out.println("CompareA:杭州");}}public interface CompareB {default void method3(){System.out.println("CompareB:上海");}}//父类public class SuperClass {public void method3(){System.out.println("SuperClass:北京");}}//子类、实现类class SubClass extends SuperClass implements CompareA,CompareB{public void method2(){System.out.println("SubClass:上海");}public void method3(){System.out.println("SubClass:深圳");}// 知识点 5:如何在子类(或实现类)的方法中调用父类、接口中被重写的方法public void myMethod(){method3(); //调用自己定义的重写的方法super.method3(); //调用的是父类中声明的// 调用接口中的默认方法CompareA.super.method3();CompareB.super.method3();}}//测试类public class SubClassTest {public static void main(String[] args) {SubClass s = new SubClass();// s.method1();// SubClass.method1();// 知识点1:接口中定义的静态方法,只能通过接口来调用CompareA.method1();// 知识点2:通过实现类的对象,可以调用接口中的默认方法// 如果实现类重写了接口中的默认方法,调用时,仍然调用的是重写以后的方法s.method2();// 知识点 3:如果子类(或实现类)继承的父类和实现的接口中声明了同名同参数的默认方法,// 那么子类在没有重写此方法的情况下,默认调用的是父类中的同名同参数的方法。-->类优先原则// 子类中此方法重写了的话,则调用子类中此方法// 知识点 4:如果实现类实现了多个接口,而这多个接口中定义了同名同参数的默认方法,// 那么在实现类没有重写此方法的情况下,报错。-->接口冲突。// 这就需要我们必须在实现类中重写此方法s.method3(); }}
三、异常
空指针异常:java.lang.NullPointerException
数组角标越界异常:ArrayIndexOutOfBoundsExcetion
1、异常概述
(1)即使把代码写得完美,在系统的运行过程中仍然会遇到一些问题,因为很多问题不是靠代码能够避免
的,比如:客户输入数据的格式,读取文件是否存在,网络是否始终保持通畅等等
在Java语言中,将程序执行中发生的不正常情况称为“异常”(开发过程中的语法错误和逻辑错误不是异常)
(2)Java程序在执行过程中所发生的异常事件可分为两类
Error:Java虚拟机无法解决的严重问题。如:JVM系统内部错误、资源耗尽等严重情况
比如StackOverflowError和OOM。一般不编写针对性的代码进行处理
public static void main(String[] args) {
// 栈溢出:java.lang.StackOverflowError
// main(args);
// 堆溢出:java.lang.OutOfMemoryError
// Integer[] arr = new Integer[102410241024];
}
Exception:其它因编程错误或偶然的外在因素导致的一般性问题,可以使用针对性的代码进行
处理,例如空指针访问、试图读取不存在的文件、网络连接中断、数组角标越界
(3)对于这些错误,一般有两种解决方法:
一是遇到错误就终止程序的运行
二是由程序员在编写程序时,就考虑到错误的检测、错误消息的提示,以及错误的处理
捕获错误最理想的是在编译期间,但有的错误只有在运行时才会发生,比如除数为0,数组下标越界等
(4)异常分类:编译时异常和运行时异常
运行时异常:是指编译器不要求强制处置的异常,一般是指编程时的逻辑错误,是程序员应该积极
避免其出现的异常,java.lang.RuntimeException类及它的子类都是运行时异常
对于这类异常,可以不作处理,因为这类异常很普遍,若全处理可能会对程序的可读性
和运行效率产生影响
编译时异常:是指编译器要求必须处置的异常,即程序在运行时由于外界因素造成的一般性异常
编译器要求Java程序必须捕获或声明所有编译时异常
对于这类异常,如果程序不处理,可能会带来意想不到的结果
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-E8Z3QHp2-1645784029180)(C:\Users\13520\AppData\Roaming\Typora\typora-user-images\image-20220212150440689.png)]
2、常见异常
import java.io.File;
import java.io.FileInputStream;
import java.util.Date;
import java.util.Scanner;
import org.junit.Test;java异常体系结构 java.lang.Throwable(1)java.lang.xxxxxError:错误,一般不编写针对性的代码进行处理(2)java.lang.xxxxxException:异常,可以进行异常处理1)编译时异常(checked)IOEXceptionFileNotFoundException //wClassNotFoundException2)运行时异常(unchecked)NullPointerException 空指针异常ArrayIndexOutOfBoundsException 数组角标越界异常ClassCaseException 类型转换异常NumberFormatException 数值类型异常InputMismatchException 输入不匹配异常ArithmaticException 算数异常(除数为0)面试题:常见的异常有哪些?举例说明public class ExceptionTest {// ******************以下是编译时异常***************************public void test7() { //执行javac时报错,编译时就错了File file = new File("hello.txt");FileInputStream fis = new FileInputStream(file);int data = fis.read();while(data != -1){System.out.print((char)data);data = fis.read();} fis.close();}// ******************以下是运行时异常***************************// ArithmeticExceptionpublic void test6() {int a = 10;int b = 0;System.out.println(a / b);}// InputMismatchExceptionpublic void test5() {Scanner scanner = new Scanner(System.in);int score = scanner.nextInt();System.out.println(score);scanner.close();}// NumberFormatExceptionpublic void test4() {String str = "123";str = "abc";int num = Integer.parseInt(str);}// ClassCaseExceptionpublic void test3() {Object obj = new Date();String str = (String)obj;}// ArrayIndexOutOfBoundsExceptionpublic void test2() {int[] arr = new int[10];System.out.println(arr[10]);String str = "abc";System.out.println(str.charAt(3));}// NullPointerExceptionpublic void test1() {int[] arr = null;System.out.println(arr[3]);String str = "abc";str = null;System.out.println(str.charAt(0));}
}
3、异常处理机制一:try-catch-finally(出现异常,自己解决掉了)
(1)try:捕获异常的第一步是用try{…}语句块选定捕获异常的范围,将可能出现异常的代码放在try语句块中
(2)catch(Exceptiontypee):在catch语句块中是对异常对象进行处理的代码,每个try语句块可以伴随一个
或多个catch语句,用于处理可能产生的不同类型的异常对象
捕获异常的有关信息:与其它对象一样,可以访问一个异常对象的成员变量或调用它的方法
getMessage() 获取异常信息,返回字符串
printStackTrace() 获取异常类名和异常信息,以及异常出现在程序中的位置,返回值void
(3)finally:捕获异常的最后一步是通过finally语句为异常处理提供一个统一的出口,使得在控制流转到程序
的其它部分以前,能够对程序的状态作统一的管理
不论在try代码块中是否发生了异常事件,catch语句是否执行,catch语句是否有异常,catch
语句中是否有return,finally块中的语句都会被执行
finally语句和catch语句是任选的
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-zawRgC2G-1645784029181)(C:\Users\13520\AppData\Roaming\Typora\typora-user-images\1856fe718d55f0f2180950b88deb622d.png)]
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-FRoZjwRs-1645784029182)(C:\Users\13520\AppData\Roaming\Typora\typora-user-images\8f4596135485e78881d6ee1cba3026b9.png)]
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import org.junit.Test;异常的处理:抓抛模型过程一“抛”:程序在征程执行过程中,一旦出现异常就会在异常代码处生成一个对应异常类的对象,并将此对象抛出一旦抛出对象以后,其后的代码就不再执行关于异常对象的产生:①系统自动生成的异常对象②手动的生成一个异常对象,并抛出throw
过程二:“抓”:可以理解为异常的处理方式:① try-catch-finally ② throwstry-catch-finally的使用try{//可能出现异常的代码}catch(异常类型1 变量名1){//处理异常的方式1 可有多个方法}catch(异常类型2 变量名2){//处理异常的方式2}catch(异常类型3 变量名3){//处理异常的方式3}...finally{//一定会执行的代码}说明:1.finally是可选的。2.使用try将可能出现异常代码包装起来,在执行过程中,一旦出现异常,就会生成一个对应异常类的对象,根据此对象的类型,去catch中进行匹配。3.一旦try中的异常对象匹配到某一个catch时,就进入catch中进行异常的处理。一旦处理完成,就跳出当前的try-catch结构(在没有写finally的情况)。继续执行其后的代码。4.catch中的异常类型如果没有子父类关系,则谁声明在上,谁声明在下无所谓。catch中的异常类型如果满足子父类关系,则要求子类一定声明在父类的上面。否则,报错5.常用的异常对象处理的方式: ① String getMessage() ② printStackTrace()6.在try结构中声明的变量,再出了try结构以后,就不能再被调用,例65行:System.out.println(num);7.try-catch-finally结构可以嵌套 体会1:使用try-catch-finally处理编译时异常,是得程序在编译时就不再报错,但是运行时仍可能报错。相当于我们使用try-catch-finally将一个编译时可能出现的异常,延迟到运行时出现。体会2:开发中,由于运行时异常比较常见,所以我们通常就不针对运行时异常编写try-catch-finally了。针对于编译时异常,我们说一定要考虑异常的处理public class ExceptionTest1 {@Testpublic void test2(){try{File file = new File("hello.txt");FileInputStream fis = new FileInputStream(file);int data = fis.read();while(data != -1){System.out.print((char)data);data = fis.read();} fis.close();}catch(FileNotFoundException e){ //可能出现的异常类型e.printStackTrace();}catch(IOException e){ //可能出现异常的类型e.printStackTrace();}}public void test1(){ String str = "123";str = "abc";try{int num = Integer.parseInt(str); System.out.println("hello-----1"); //上一行如果出现异常了,不会执行这一行}catch(NumberFormatException e){
// System.out.println("出现数值转换异常了,不要着急....");
// String getMessage():
// System.out.println(e.getMessage()); getMessage()方法返回一个字符串,所以打印看
// printStackTrace(): 这个方法返回void,故直接调用e.printStackTrace();}catch(NullPointerException e){System.out.println("出现空指针异常了,不要着急....");}catch(Exception e){System.out.println("出现异常了,不要着急....");}
// System.out.println(num); 在try结构中声明的变量,再出了try结构以后,就不能再被调用System.out.println("hello----2");}
}try-catch-finally中finally的使用:1.finally是可选的。2.finally中声明的是一定会被执行的代码。即使catch中又出现异常了,try中有return语句,catch中有return语句等情况3.像数据库连接、输入输出流、网络编程Socket等资源,JVM是不能自动的回收的,我们需要自己手动的进行资 源的释放,此时的资源释放,就需要声明在finally中
public class FinallyTest {public void test2() {FileInputStream fis = null;try {File file = new File("hello1.txt"); //文件可能不存在,而出现异常fis = new FileInputStream(file);int data = fis.read();while (data != -1) {System.out.print((char) data);data = fis.read();}} catch (FileNotFoundException e) {e.printStackTrace();} catch (IOException e) {e.printStackTrace();} finally {try {if (fis != null)fis.close();} catch (IOException e) {e.printStackTrace();}}}public void testMethod() {int num = method();System.out.println(num);}public int method() {try {int[] arr = new int[10];System.out.println(arr[10]);return 1;} catch (ArrayIndexOutOfBoundsException e) {e.printStackTrace();return 2;} finally {System.out.println("我一定会被执行");return 3;}}public void test1() {try {int a = 10;int b = 0;System.out.println(a / b);} catch (ArithmeticException e) {// e.printStackTrace();int[] arr = new int[10];System.out.println(arr[10]);} catch (Exception e) {e.printStackTrace();}// System.out.println("我好慢呀~~~"); 出现异常的话异常那句后面的都不会被执行finally {System.out.println("我好慢呀~~~");}}
}
4、异常处理机制二:throws+异常类型
(出现异常,自己无法解决,喊别人过来帮忙,或层层报到上级,如果都不行,则爆了) 即向上抛异常
(1)如果一个方法(中的语句执行时)可能生成某种异常,但是并不能确定如何处理这种异常,则此方法应
显示地声明抛出异常,表明该方法将不对这些异常进行处理,而由该方法的调用者负责处理
在方法声明中用throws语句可以声明抛出异常的列表,throws后面的异常类型可以是方法中产生的
异常类型,也可以是它的父类
(2)"throws + 异常类型"写在方法的声明处,指明此方法执行时,可能会抛出的异常类型
一旦当方法体执行时,出现异常,仍会在异常代码处生成一个异常类的对象,此对象满足throws后异常
类型时,就会被抛出,异常代码后续的代码,就不再执行
(3)关于异常对象的产生:① 系统自动生成的异常对象
② 手动生成一个异常对象,并抛出(throw)
(4)try-catch-finally的方式是真正的将异常给处理掉了
throws的方式只是将异常抛给了方法的调用者,并没有真正将异常处理掉
public class ExceptionTest2 { public static void main(String[] args){try {method2();} catch (IOException e) {e.printStackTrace();} method3();}public static void method3(){try {method2();} catch (IOException e) {e.printStackTrace();}}public static void method2() throws IOException{ //抛给method2method1();}//编译时异常public static void method1() throws FileNotFoundException,IOException{File file = new File("hello1.txt"); //这里就抛出了FileInputStream fis = new FileInputStream(file);int data = fis.read();while(data != -1){System.out.print((char)data);data = fis.read();} fis.close();System.out.println("hahaha!"); //这里不会被执行}
}重写方法声明抛出异常的原则
方法重写的规则之一:子类重写的方法抛出的异常类型不大于父类被重写的方法抛出的异常类型import java.io.FileNotFoundException;
import java.io.IOException;
public class OverrideTest {public static void main(String[] args) {OverrideTest test = new OverrideTest();test.display(new SubClass());} public void display(SuperClass s){try {s.method();} catch (IOException e) { //多态,所以要小于e.printStackTrace();}}
}
class SuperClass{public void method() throws IOException{ }
}
class SubClass extends SuperClass{public void method()throws FileNotFoundException{ }
}
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-onFPKzaa-1645784029184)(C:\Users\13520\AppData\Roaming\Typora\typora-user-images\a349a3eb15bb86490d6c86c2b2a667ac.png)]
(6)开发中如何选择使用try-catch-finally 还是使用throws?
----如果父类中被重写的方法没有throws方式处理异常,则子类重写的方法也不能使用throws,意味着
如果子类重写的方法中有异常,必须使用try-catch-finally方式处理
----执行的方法a中,先后又调用了另外的几个方法,这几个方法是递进关系执行的,建议这几个方法使
用throws的方式进行处理,而执行的方法a可以考虑使用try-catch-finally方式进行处理
5、手动抛出异常
Java异常类对象除在程序执行过程中出现异常时由系统自动生成并抛出,也可根据需要使用人工创建并抛出
首先要生成异常类对象new Exception,然后通过throw语句实现抛出操作throw new Exception,交给Java
运行环境
可以抛出的异常必须是Throwable或其子类的实例
public class StudentTest {public static void main(String[] args) {try {Student s = new Student(); //try-catch把异常处理掉了s.regist(-1001);System.out.println(s);} catch (Exception e) {
// e.printStackTrace();System.out.println(e.getMessage());}}
}
class Student{private int id;public void regist(int id) throws Exception{ //这行是异常处理if(id > 0){this.id = id;}else{
// System.out.println("您输入的数据非法!");
// 手动抛出异常
// throw new RuntimeException("您输入的数据非法!");throw new Exception("您输入的数据非法!"); //这行是生成异常对象}}@Overridepublic String toString() {return "Student [id=" + id + "]";}
}
6、用户自定义异常类
(1)一般地,用户自定义异常类都是RuntimeException的子类
自定义异常类通常需要编写几个重载的构造器
自定义异常需要提供serialVersionUID
自定义的异常通过throw抛出
自定义异常最重要的是异常类的名字,当异常出现时,可以根据名字判断异常类型
(2)如何自定义异常类? (模仿官方给出的异常类)
1)继承于现有的异常结构:RuntimeException 、Exception
2)提供全局常量:serialVersionUID (对类的标识)
3)提供重载的构造器
public class MyException extends RuntimeException{static final long serialVersionUID = -7034897193246939L; public MyException(){} public MyException(String msg){super(msg);}
}
7、异常练习
public class ReturnExceptionDemo {static void methodA() {try {System.out.println("进入方法A"); //输出"进入方法A"throw new RuntimeException("制造异常"); // "用A方法的finally"} finally { // "制造异常"(因为e.getMessage())System.out.println("用A方法的finally");}}static void methodB() {try {System.out.println("进入方法B"); //输出"进入方法B"return; //"调用B方法的finally" (因为finally一定要执行)} finally {System.out.println("调用B方法的finally");}}public static void main(String[] args) {try {methodA();} catch (Exception e) {System.out.println(e.getMessage()); //输出异常获取的信息"制造异常"}methodB();}
}/*
编写应用程序EcmDef.java,接收命令行的两个参数,要求不能输入负数,计算两数相除
对数据类型不一致(NumberFormatException)、缺少命令行参数(ArrayIndexOutOfBoundsException)、
除0(ArithmeticException)及输入负数(EcDef自定义的异常)进行异常处理
*/
public class EcmDef {public static void main(String[] args) {try {int i = Integer.parseInt(args[0]);int j = Integer.parseInt(args[0]);int result = ecm(i,j);System.out.println(result);} catch (NumberFormatException e) {System.out.println("数据类型不一致");}catch (ArrayIndexOutOfBoundsException e){System.out.println("缺少命令行参数");}catch (ArithmeticException e){System.out.println("除0");}catch (EcDef e) { //和抛出的异常对象对应System.out.println(e.getMessage()); //和抛出自定义异常一体}}public static int ecm(int i, int j) throws EcDef{ //声明方法可能要抛出的各种异常类if(i < 0 || j < 0){throw new EcDef("分子或分母为负数了!"); //抛出自定义的异常,生成了异常对象} //生成了才能抛return i / j;}
}
//自定义异常类
public class EcDef extends Exception {static final long serialVersionUID = -33875164229948L;public EcDef() {}public EcDef(String msg) {super(msg);}
}
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-HDPwOFDT-1645784029185)(C:\Users\13520\AppData\Roaming\Typora\typora-user-images\4614aa28f89b84571c65636790a8f087.png)]
四、设计模式
设计模式是在大量的实践中总结和理论化之后优选的代码结构、编程风格、解决问题的思考方式
设计模式就像是经典的棋谱,不同的棋局用不同的棋谱,即所谓“套路”
共有23种设计模式:创建型模式(5种)、结构型模式(7种)、行为型模式(11种)
MVC主要由3个模式组合而成:组合模式,策略模式和观察者模式[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-P7B44cBL-1645784029186)(C:\Users\13520\AppData\Roaming\Typora\typora-user-images\20150623085812416.jpg)]没有把MVC提及为一种设计模式,而是把它当做“一组用于构建用户界面的类集合”,是因为它其实是其它三个经典的设计模式的演变:观察者模式,策略模式和组合模式,根据MVC在框架中的实现不同可能还会用到工厂模式和装饰器模式
-
经典MVC模式在Web上的应用,就产生了现在我们看到的Web三层结构
MVC设计模式,常用的设计模式之一,将整个程序分为三个层次:视图模型层、控制器层、数据模型层,
将程序输入输出、数据处理、数据展示分离开来
(1)模型层model:处理数据 —数据对象封装 model.bean/domain
(进行业务逻辑判断) —数据库操作类 model.dao
(数据库存取) —数据库 model.db
(2)控制层controller:处理业务逻辑—controller.activity 应用界面相关
(起始点,用户输入,人机交互) —controller.fragment 存放fragment
(将用户输入的指令和 —controller.adapter 显示列表的适配器
数据传递给业务模型) —controller.service 服务相关的
—controller.base 抽取的基类
(3)视图层view:显示数据 —view.utils 相关工具类
(根据业务逻辑选不同视图) —view.ui 自定义view [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-dzBxmY2o-1645784029187)(C:\Users\13520\AppData\Roaming\Typora\typora-user-images\image-20220202132327273.png)]
web管理系统中通用的结构
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-cUgIajqi-1645784029187)(C:\Users\13520\AppData\Roaming\Typora\typora-user-images\20150620222019834.jpg)](1)实体类
实体类就是数据库表的映射,即一个实体类表示数据库中的一张表,每个属性实现get和set方法
在系统分析与设计阶段,类通常可以分为三种,分别是实体类、控制类和边界类
①实体类:实体类对应系统需求中的每个实体,它们通常需要保存在永久存储体中,一般使用数据库表
或文件来记录,实体类既包括存储和传递数据的类,还包括操作数据的类。实体类来源于需
求说明中的名词,如学生、商品等。
②控制类:控制类用于体现应用程序的执行逻辑,提供相应的业务操作,将控制类抽象出来可以降低界
面和数据库之间的耦合度。控制类一般是由动宾结构的短语(动词+名词)转化来的名词,
如增加商品对应有一个商品增加类,注册对应有一个用户注册类等。
③边界类:边界类用于对外部用户与系统之间的交互对象进行抽象,主要包括界面类,如对话框、窗
口、菜单等
(2)数据库操作类
数据库操作类就是DBUtility/SqlHelper,这里通常是针对接口编程的,已应对不同数据库,通常项目中,
使用数据库连接池来管理数据库连接,海康的项目中就采用了这些技术。
(3)数据库访问层DAL
数据库访问层实现了对每张数据库表的增,删,改,查操作。调用数据库操作类提供的接口。
(4)业务逻辑层BLL
业务逻辑层就是实现了与项目有关的具体业务,调用DAL层提供的接口javaweb中的MVC:jsp视图、servlet控制器、javabean模型
在java Web项目中,通常将javabean模型的每个部分命名为service:对应业务逻辑层 BLL
dao:对应数据访问层 DAL
domain:对应实体类 Entity
DBUtility:对应数据库连接类[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-zDK51Db4-1645784029188)(C:\Users\13520\AppData\Roaming\Typora\typora-user-images\InkedInked20150620221015902_LI.jpg)]
web与经典MVC的区别:Web中视图和控制器不是真正意义上的策略模式,视图和模型之间也不是真正意义
上的观察者模式,所以Web无法实现真正意义上的MVC -
设计一个客户信息管理软件
com.xueyu.p.bean —Customer.java // 实体对象,用来封装客户信息
com.xueyu.p.service—CustomerList.java // 为Customer对象的管理模块,内部用数组管理一组
Customer对象,并提供添加修改等操作,供CustomerView调用
添加修改等方法返回true、false等,都是逻辑判断,以类整体为
对象操作,不需要用属性方法等,只用类对象这个整体
com.xueyu.p.ui —CustomerView.java //为主模块,负责菜单的显示和处理用户操作
com.xueyu.p.util —CMUtility.java //工具类,用于界面菜单的选择,读取键盘的字符,按方法返回 -
单例设计模式:singleton
即采取一定的方法保证在整个系统中,对某个类只能存在一个对象实例并且该类只提供一个取得其对象
实例的方法
如果我们要让类在一个虚拟机中只能产生一个对象,我们首先必须将类的构造器的访问权限设置为private,
这样,就不能用 new 操作符在类的外部产生类的对象了,但在类内部仍可以产生该类的对象,因为在类
的外部开始还无法得到类的对象,只能调用该类的某个静态方法以返回类内部创建的对象,静态方法只能
访问类中的静态成员变量,所以,指向类内部产生的该类对象的变量也必须定义成静态的单例模式的实现:饿汉式和懒汉式 //饿汉式class Bank{//1.私有化类的构造器private Bank() {}//2.内部创建类的对象//4.要求此对象也必须声明为静态的private static Bank instance = new Bank();//3.提供公共的静态的方法,返回类的对象public static Bank getInstance() {return instance;}}Bank bank1 = Bank.getInstance();Bank bank2 = Bank.getInstance(); //bank1和bank2相等//懒汉式class Bank{//1.私有化类的构造器private Bank() {}//2.声明当前类对象,没有初始化//4.要求此对象也必须声明为静态的private static Bank instance = null;//3.声明public、static的返回当前类对象的方法public static Bank getInstance() {if(instance==null){ //多线程同时进入if判断,都是null,则都去new了,不安全instance = new Bank();}return instance;}}Bank bank1 = Bank.getInstance();Bank bank2 = Bank.getInstance(); //bank1和bank2相等
区分饿汉式和懒汉式
饿汉式:坏处—对象加载时间过长(不需要使用,但是一上来就加载,加载时间过长)
好处—饿汉式是线程安全的
懒汉式:好处—延迟对象的创建
坏处—懒汉式线程不安全单例模式的优点:由于单例模式只生成一个实例,减少了系统性能开销,当一个对象的产生需要比较多的资源
时,如读取配置、产生其他依赖对象时,则可以通过在应用启动时直接产生一个单例对象,
然后永久驻留内存的方式来解决单例模式应用场景:(1)网站的计数器:一般也是单例模式实现,否则难以同步。
(2)应用程序的日志应用:一般都使用单例模式实现,这一般是由于共享的日志文件一直
处于打开状态,因为只能有一个实例去操作,否则内容不好追加。
(3)数据库连接池的设计:一般也是采用单例模式,因为数据库连接是一种数据库资源。
(4)项目中,读取配置文件的类,一般也只有一个对象,没有必要每次使用配置文件数据,
都生成一个对象去读取。
(5)Application也是单例的典型应用
(6)Windows 的 任务管理器就是很典型的单例模式,只有一个任务管理器
(7)Windows 的 回收站也是典型的单例应用。在整个系统运行过程中,回收站一直维护着
仅有的一个实例 -
代理模式Proxy (接口的应用)
代理设计就是为其他对象提供一种代理以控制对这个对象的访问public class NetWorkTest {public static void main(String[] args) { Server server = new Server(); // server.browse();ProxyServer proxyServer = new ProxyServer(server); proxyServer.browse();} } interface NetWork{public void browse(); }//被代理类 class Server implements NetWork{@Overridepublic void browse() {System.out.println("真实的服务器来访问网络");} }//代理类 class ProxyServer implements NetWork{ private NetWork work;public ProxyServer(NetWork work){this.work = work;}public void check(){System.out.println("联网前的检查工作");}@Overridepublic void browse() { check(); work.browse(); //通过代理类对象调用} }
应用场景:安全代理:屏蔽对真实角色的直接访问。
远程代理:通过代理类处理远程方法调用(RMI)
延迟加载:先加载轻量级的代理对象,真正需要再加载真实对象
比如你要开发一个大文档查看软件,大文档中有大的图片,有可能一个图片有 100MB,在打开文
件时,不可能将所有的图片都显示出来,这样就可以使用代理模式,当需要查看图片时,用 proxy
来进行大图片的打开
分类:静态代理(静态定义代理类)
动态代理(动态生成代理类)
JDK 自带的动态代理,需要反射等知识public class StaticProxyTest {public static void main(String[] args) {Proxy s = new Proxy(new RealStar());s.confer();s.signContract();s.bookTicket();s.sing();s.collectMoney();} } interface Star {void confer();// 面谈void signContract();// 签合同void bookTicket();// 订票void sing();// 唱歌void collectMoney();// 收钱 }//被代理类 class RealStar implements Star {public void confer() {}public void signContract() {}public void bookTicket() {}public void sing() {System.out.println("明星:歌唱~~~");}public void collectMoney() {} }//代理类 class Proxy implements Star {private Star real;public Proxy(Star real) {this.real = real;}public void confer() {System.out.println("经纪人面谈");}public void signContract() {System.out.println("经纪人签合同");}public void bookTicket() {System.out.println("经纪人订票");}public void sing() {real.sing();}public void collectMoney() {System.out.println("经纪人收钱");} }
五、Java并发
线程池、SYNC和Lock锁机制、线程通信、volatile、ThreadLocal、CyclicBarrier、Atom包、CountDownLatch、AQS、CAS原理等等