这周打码比较少,大部分时间在总结和思考,同时根据发现的知识漏洞和新的技术需求去学新的知识,相比上周,作息上轻松了一点,也能抽空去跑跑步,打打球,夏天一来,就容易想起高中时那种在球场上挥汗如雨的感觉,那时球场上空气的味道,场下看球的人的声音,都是无法磨灭的青春印记了
- 对前后端分离有了新的理解
- 了解到EL表达式可以简化jsp中的Java代码
- 了解到JSTL标签库
- 了解到并发请求会导致线程安全问题
- 了解到postman开发工具和chrome调试工具
- 认识到servlet和jsp的一体两面性和jsp中嵌入的三种代码的区别
- 尝试使用外键多表关联
- 尝试使用联表查询,尝试联表插入
- 尝试使用Properties类将mysql配置信息转移到配置文件
- 尝试写文档注释,并使用Javadoc导出API文档
- 阅读了阿里巴巴开发手册,尝试按照规范写代码
- 尝试手写简单的数据库连接池
- 尝试在idea中配置git仓库并使用idea直接将代码推送到远程仓库
- 尝试使用工厂模式(中途跑去学IO流,还没实现)
- 关于Java中异常分类和处理的思考
- 关于RBAC基于角色权限控制的思考
- 关于静态类和单例模式的思考
- 学习使用Md5加密,了解base64编码
- 学习Java中FIle类与IO流
- 学习泛型的使用
- 学习类加载器和反射的使用
- 学习使用er图建立数据模型
阅读和参考的文章:
1.Java异常区分和处理的一些经验分享_java_脚本之家 2.Java异常处理的最佳实践 - 个人文章 - SegmentFault 思否 3.异常实践
异常是由某些原因引起的程序无法正确执行的情况,如果没有异常处理,将导致程序停止运行,异常处理就是让程序能够继续执行的应对方案,Java异常机制的本意是将异常处理代码和逻辑代码分离
-
unchecked异常是由于代码,逻辑不严谨,或者运行环境的变动,导致运行时意外发生的异常,这种异常一旦发生,通常用户无法解决,应当停止程序运行,这种异常不需要程序员提供解决方案,如果是error,应该由jvm的编写者处理,如果是runtime exception,应该由程序员对代码进行改进(不是写新的代码来等着这个异常发生然后处理,而是改写代码避免异常的发生)
-
checked异常是程序员无法干涉的,用户的不当行为导致的异常,这种异常发生之后通常没有必要停止程序运行,可以通过引导用户行为来修复,或者准备降级的应对措施来处理的,编译器要求程序员对此显式地编写处理异常的代码
- 如果调用者可以处理这个异常,使异常场景被修复,比如重试,访问备用资源等等替代方案,即使得这个异常的影响可以被消除或者削弱,那么抛出checked exception,要求调用方对此异常采取解决方案。
- 如果调用方无法处理这个异常,比如运行环境出了问题,资源失效,SQL异常,下层代码不严谨导致的意外等调用方无法解决,只能让程序停止或者向用户输出提示的异常,那么抛出unchecked exception,调用方可以选择捕获这个异常,也可以选择什么也不做。
- 如果没有打算向调用者提供更多的功能,并且调用方无法处理这个异常,那么直接抛出new runtime exception(),避免污染上层代码。
- 只有在需要向上层代码提供更多信息,比如返回产生异常的用户id,返回可用的资源名称等自定义功能时,创建一个自定义异常,并提供这些自定义方法
- 不填cause时,如果异常发生,堆栈信息只能跟踪到从调用到抛出的过程
- 填了cause,则可以将上层异常包含进来,得到上层异常的跟踪堆栈
- 只需要在最外层代码打印异常日志,否则在方法互相调用中会导致重复打印同一段日志,加大后期在日志中排查问题的难度
- 一般而言,在service层处理异常,决定向用户输出提示,让用户重试,或者终止程序
在用户的信息中简单的添加一个字段,标识为用户或者管理员,不是很好的选择,因为这样的权限管理是一对一的关系,一个用户在同一时刻只能有一种权限,这样的管理方式只适合以下的情况:
- 上层的权限完全覆盖下层的权限,比如管理员默认拥有普通用户的所有权限
- 某种用户类型所拥有的权限是固定的
基于角色的权限管理(role-based access control),主要用于复杂的权限管理。
- 至少需要的表:用户表,角色表,权限表,用户-角色中间表,角色-权限中间表。
- 基本结构:一个用户可以对应多个角色,一个角色可以有多种权限,用户和角色之间,角色和权限之间通过中间表
- 权限:操作对象和操作,即对什么对象做什么
- 角色:权限的组合,一个角色对应一组权限
- 用户:角色的组合,一个用户对应一组角色
类并不是执行构造方法后才被实例化,构造方法的作用是对成员属性进行初始化,在执行构造方法前,对象就已经被创建,因此构造方法中可以使用this关键字了
- 静态类中是静态方法,无法被覆盖重写,单例模式中非静态方法可以覆盖重写
- 静态类没有实例,单例模式有且只有一个实例
- 静态类的初始化在类加载时完成,单例模式的初始化可以延迟到第一个实例创建时
- 静态类体现的是面向过程的思想,通常是各种工具类,而单例模式是面向对象的思想,如果要实现接口,继承,多态等面向对象的特性,则使用单例模式(登记式单例才可以,静态方法提供单例不行)
用于为应用程序提供信息摘要算法的功能,如 MD5 或 SHA 算法
md5算法是一种消息摘要算法,即经过加密之后的信息是不完整的,由加密后的信息无法获得原始数据,因此md5只能单向加密,无法根据加密后的结果还原信息,md5对于同一段明文的加密结果相同,因此可以用来验证消息的完整性。
Base64是使用用64个字符来表示任意二进制数据的方法,支持编码和解码,md5加密的结果是128位(16字节)的二进制数据,因此使用Base64对md5加密结果进行编码,得到只由大小写字母各26个,加上10个数字,和加号“+”,斜杠“/”,一共64个字符表示的数据(等号“=”用来作为后缀)。
public class MD5Util {
private MD5Util instance = new MD5Util();
private final String ENCODING = "UTF-8";
private MD5Util() {
}
public MD5Util getInstance() {
return this.instance;
}
public String getDigest(String originText) {
MessageDigest md = null;
byte[] digest = null;
try {
// 1. 获取实例
md = MessageDigest.getInstance("md5");
// 2. 调用digest方法,传入参数和返回值都是byte数组
digest = md.digest(originText.getBytes(ENCODING));
} catch (NoSuchAlgorithmException e) {
throw new RuntimeException("无法支持md5加密", e);
} catch (UnsupportedEncodingException e) {
throw new RuntimeException("无法支持UTF-8编码格式",e); }
}
// 3.使用Base64编码将二进制数据转成字符串
return Base64.getEncoder().encodeToString(digest);
}
}
- File是Java中用来代表文件/文件夹的一个对象
- File不等于文件,只是文件的抽象,Flie作为一个Java类的实例而存在,不依赖于具体的文件,只是Java把对文件的操作抽象成为对File对象的操作。
- File对象可以实现对文件的创建,删除,修改等操作,但是无法进行文件内容的读取,只能依靠IO流.
- 绝对路径:带盘符的完整路径
- 相对路径:不写盘符,默认以项目位置为当前路径
- 通过一个文件路径创建一个File对象
- 通过一个文件路径+文件名/文件夹名创建一个File对象
- 通过一个File对象+文件名/文件夹名创建一个File对象
其中第三种相当于把父目录封装成File对象,再根据文件名/文件夹名创建File对象
创建:如果目标文件/文件夹已存在,就不创建了
- createNewFile:创建一个文件,如果目录不存在则无法创建
- mkdir:创建一个文件夹,如果目录不存在则无法创建
- mkdirs:创建文件夹,如果目录不存在则创建多级文件夹
删除:如果目标文件夹内有文件/文件夹,则无法删除
- delete:删除一个文件/文件夹
修改:
- renameTo(File newfile):重命名一个文件/文件夹,相当于把文件转移到另一个File对象所指的路径
另有获取文件名,文件是否存在/可读/可写等方法
- list方法:获取当前目录下的文件名数组
- listFiles方法:获取当前目录下的文件数组
主要思想:过滤器模式和模板模式
创建一个过滤器,通过重写接口的accept方法传递策略,list方法在遍历文件名时使用过滤器去除不符合条件的文件
FilenameFilter接口是单函数接口,因此可以使用Lambda表达式简化代码
- 程序中数据的输入输出,被抽象为流, 按照相对于程序的流向,可分为输出流和输入流, 按照数据流的格式,可分为字节流和字符流(类似于C语言中以二进制和以字符形式读取文件)
- 另有有节点流和处理流,节点流用来包装源头,处理流的作用是对Java程序提供统一的数据输入输出的支持,并提高性能。
- Java将来自任何节点的数据流都包装成为处理流,使得可以使用相同的代码来处理来自不同设备的数据流,并可以以增加缓冲的方式提高性能,并支持一次输出大批量的内容,这便是处理流模型,思想类似于C语言中流式文件的处理方式
- 类似于C语言中二进制文件和文本文件的区别,字符其实只是一种特殊的二进制字节,是按照一定的编码方式处理之后,按照一定规则来存储信息的数据,字符在计算机中也是由二进制组成的,只不过这种二进制可以按照一种规则解码后,成为人类可以直接阅读的自然语言,而普通的二进制文件只有计算机能直接“阅读”
- 字节输入流 : InputStream
- 字符输入流 : Reader
- 字节输入流 : OutputStream
- 字符输入流 : Writer
- Java提供了从字节流到字符流的转换流,分别是InputStreamReader和OutputStreamWriter
- 字符流=字节流+编码表
- 没有从字符流到字节流的转换流
- 不管是字节流和字符流,都提供了read方法和write方法,分别用来读取和输出数据。
- 创建输入/输出流对象时的参数都是指向外部节点的对象(比如File对象)
- 调用read和write方法时的参数都是指向程序中内存空间的对象
- 文件输出操作步骤:创建输出流对象(提供输出目标)->调用write方法(提供数据源)->释放资源
- 文件输出的操作步骤可以看成:提供水管(让水管指向目标)->喷水(提供水龙头)->回收水管
- 让流对象成为垃圾,可以被垃圾回收器回收
- 通知系统去释放该文件相关的资源
- 因为Java中输出流的内容会被先保存在缓冲区,直到缓冲区满,才会真正把数据发送出去,当数据很小时,可能导致数据接收不到,而使用flush可以强制将缓冲区的数据发送出去,不必等到缓冲区满
- 一次读取一个字节数组的效率明显比一次读取一个字节的效率高,因此Java提供了带缓冲区的字节类,称为缓冲区类。BufferedInputStream和BufferedOutputStream
- 字符缓冲区类BufferedReader和BufferedWriter与之类似.
- 缓冲区类的构造方法可以指定缓冲区的大小,如果没有指定,则使用默认大小,一般默认大小已经够用了
- 缓冲区类只是提供缓冲区,实际的读写操作还是基本IO流对象来做的,因此缓冲区类的构造方法的参数是基本IO对象
- DataInputStream
- 与缓冲区流的使用方式类似,也需要传入一个实际进行读写操作的InputStream实现类,数据输入/输出流的特殊之处在于它是对Java中的各种类型数据进行输入输出
- ByteArrayInputStream
- 与FileInputStream类似,只是输入输出的对象是一段内存空间,即内存操作流内部的字节数组。相当于把内存操作流对象当成一个字节数组,对它进行输入输出。
- PrintStream/PrintWriter
- 打印流是单向的,只有输出流,没有输入流
- 可以打印任意类型的数据
- 可以启用自动刷新
- 打印流可以直接操作文本文件
- 打印流是以字节方式输出
- RandomAccessFile
- 仅可用于文件操作
- 相当于C语言中的文件读写操作,可以随机读写文件,即可以操作文件的读写指针,并且可以选择使用只读/只写等方式读写文件
- getFilePointer:得到当前文件指针位置
- seek:设置文件指针位置
- SequenceInputStream
- 用于将多个输入流合并
- ObjectOutputStream
- 用于将对象写入文件,或者在网络传输,操作的对象必须是已经实现序列化接口的(序列化接口只是一种标记接口)
- 序列化流是将对象存入文件或在网络中传输,反序列化流就是将流数据还原成为对象
- 一个类的序列化值与类的成员变量有关,如果类的成员变量改变,则序列化值也会变,一个对象被存储到文件时会记录它的类的序列化值,如果将来类的序列化值改变了,这个对象就无法被反序列化,即无法还原为对象了,因此通常生成一个serialVersionUID,用来记录当前版本的类的序列化值,以便将来反序列化以往版本的对象
- 使用translent关键字可以让一个成员变量不被序列化
- in和out时system类中的两个静态成员变量分别是标准输入流InputStream和标准输出流PrintStream,分别代表系统的标准输入输出设备,即键盘和显示器
- Scanner是JDK5之后出现的方便处理键盘输入的数据的类,本质相当于一个缓冲区,并且封装了将字节信息转成各种数据类型的方法
- Properties是一个属性集合类,是Hashtable的子类,属性列表中的键值对的键和值都是字符串
- 特殊之处:Properties可以保存在流中或者从流中加载
- 特有功能:setProperty和getProperty
- load方法:把文件中的数据读取到集合,该文件的数据必须是键值对形式的
- 相当于把数据类型方程一个参数去传递,主要的作用是把明确类型的工作推迟到创建对象或者调用方法的时候,既避免了固定类型把代码写死,又可以帮助编译器检查数据类型,避免不兼容的类型转换导致运行时出错。
- 泛型技术允许在定义类,接口,方法时使用类型形参
- 泛型定义在类/接口/方法上,可以用泛型代替返回值类型,但是定义泛型名称与定义参数和返回值类型是独立的,泛型是一种类型形参,但是该形参不是方法的形参的一员,泛型形参写在<>中
- 可以把泛型定义在接口上当时,还不能代表任意类型,此泛型能代表的类型取决于该接口的实现类,如果实现类同样使用了泛型时,就可以代表任意类型
- 三种通配符:?,?extends E , ? super E
- 如果不使用通配符,泛型在创建对象时左右必须一致,即使右边泛型是左边泛型的子类也不行
- 使用‘?’可以表示任意类型,表示在右边创建对象时才确定类型
- ‘?extends E’和‘? super E'都是具有限定条件的通配符,分别是向下限定为E及其子类,和向上限定为E及其父类,其中E是一个具体的类型
- 当第一次使用某个类时,会对该类进行加载,连接,初始化三步操作
- 加载就是把class文件加载到内存中,并为之创建一个Class对象
- Bootstrap ClassLoader根类加载器:负责java核心类的加载
- Extension ClassLoader扩展加载器:负责jre扩展目录的jar包的加载
- System ClassLoader系统类加载器:负责加载Java程序的类和classpath中的类
- 反射机制是指在运行状态下,都能动态地获取任意一个类的所有属性和方法,并且能够调用任意一个对象的任意方法和属性,只需要拿到这个类的字节码文件对象,即其对应的Class类的对象
- 通过反射动态获取类与直接导入一个类的包是不一样的,直接导入包是在编译时就决定了具体的类,而使用反射,可以把具体的类推迟到运行时才确定,也就可以通过使用配置文件动态地修改具体地类,工厂模式和ORM模式就是使用了反射技术
- Field : 用来存储成员变量的类
- Constructor : 用来存储构造方法的类
- Method : 用来存储成员方法的类
- 反射是通过一个对象的类,得到Class的Filed,Constructor,Method等成员变量所指的对象,然后通过这些Filed,Constructo,Method对象,反向地操作这个对象。相当于是原本作为成员而被调用的Field,Constructor,Method等被调用者反过来作为调用者调用了对象的成员,也可看成调用权通过Class对象的反射而转移了
- 代理主要是对外提供统一的接口,而在内部的具体功能的实现,是由具体的类实现的
- 动态代理时代理模式的升级,与代理模式不同的是,动态代理的目标对象是在运行时才确定的,这同样得益于反射技术的运用
- 这个接口的实现负责接收一个目标对象,返回一个代理对象,在这个过程中调用具体的实现类实现具体功能,并且提供代理特有的功能,调用的过程就是通过反射实现的。
- 学习和使用正则表达式
- 学习数据库事物管理,索引的使用
- 复习容器类和继承与多态的内容
- 实践使用RBAC权限管理
- 实践使用反射,泛型和IO流技术写出更具通用性的代码
- 学习jsp和servlet更多的内容