JDK9模块化知识和规则入门

Posted m0_69526086

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了JDK9模块化知识和规则入门相关的知识,希望对你有一定的参考价值。

  • JEP 282: Jlink: The Java Linker

  • JSR 376: Java Platform module System

  • JSR 379: Java SE 9

现在让我们比较一下Java8和Java9应用程序。

比较Java8和Java9应用程序

=====================

您已经使用版本5、6、7和8开发了许多Java应用程序,因此您可能非常了解9之前的Java应用程序的外观及其包含的组件。对于那些需要复习的人,Java SE 8应用程序:

以及Java9应用程序:

在Java8和更早的应用程序中,顶级组件是包 package 。它将一组相关类型放入一个组中。它还包含一组资源。

java9应用程序与java8没有太大区别;它引入了一个新组件 module ,用于将一组相关的包放入一个组中。同时还介绍了另一个新组件:模块描述符module-info.java

Java8应用程序将包作为顶级组件,Java9应用程序将模块作为顶级组件。

=========================================

顺便说一下,每个Java9模块只能是一个具有一个模块描述符的模块。与Java8包不同,您不能将多个模块构建到单个模块中。

下面列出了Java 9模块中的主要组件:

  • 一个模块

  • 模块名称

  • 模块描述符

  • 成套设备

  • 类型和资源集

资源可以是模块描述符或任何其他属性或XML。

接下来,让我们深入研究模块和模块描述符。

模块和模块描述符基础

==============

现在我们将讨论模块和模块描述符基础的两个更重要的概念:语法和规则。

模块基础知识和规则

=============

在开发任何Java 9模块时,应记住以下重要的基本规则:

  • 每个模块都有一个唯一的名称

  • 每个模块在源文件中都有一些描述

  • 模块描述符文件放在顶层目录中

  • 每个模块可以有任意数量的包和类型

  • 一个模块可以依赖于任意数量的模块

每个模块都有一个唯一的名称

=================

因为模块位于JVM的全局空间中,所以每个模块都应该有一个唯一的名称。与包和JAR文件名一样,可以使用反向域名模式来定义模块名。

例如,如果您要为http://www.taman.com.eg域名开发模块,则可以使用例如: eg.com.taman.mod1 作为第一个模块名,例如: eg.com.taman.mod2 作为第二个模块名,依此类推。

每个模块在源文件中都有一些描述

===================

模块描述在名为Module的源文件中表示module-info.java应该这样命名。每个模块应该只有一个模块描述符(module-info.java).

模块描述符是一个Java文件。它不是XML、文本或属性文件。

模块描述符文件放在顶层目录中

==================

顶层目录是模块的根文件夹。

例如,如果您要开发例如: eg.com.taman.mod1 模块,则应将模块描述符放在例如: eg.com.taman.mod1 模块目录。

每个模块可以有任意数量的包和类型

====================

一个模块可以依赖于任意数量的模块。

现在,让我们看看模块描述符中有什么。

模块描述符

=========

在Java9模块中,模块描述符是包含描述模块的模块元数据的资源。它不是XML或属性文件,而是普通的Java文件。

必须将此文件命名为module-info.java并将其放在模块的根文件夹中。与其他Java源文件一样,模块文件被编译到模块中module-info类使用 javac 命令。

使用module关键字创建模块描述符:

module

// Module Meta Data goes here.

例如:

module eg.com.taman.mod1

这是一个简单而最小的模块描述符示例。让我们讨论一下模块元数据。

模块元数据

模块包含以下基本元数据:

  • 唯一的名字

  • 出口条款

  • requires子句

我将在下面的部分更深入地讨论并提供一些示例。

唯一的名字

=========

模块具有唯一的名称。使用module关键字定义模块类型,如本例所示:

module eg.com.taman.mod1

出口条款

========

模块可以将其包导出到外部世界,以便其他模块可以使用它们。在模块描述符中,使用 exports 子句将包导出到外部世界或其他模块:

module eg.com.taman.mod1

exports eg.com.taman.service;

请注意,并非强制导出所有包。由你决定导出哪一种。

requires子句

==============

模块可以导入或使用其他模块包。在模块描述符中,使用requires子句导入其他模块以使用其包:

module eg.com.taman.mod2

requires eg.com.taman.mod1;

正如你在这个例子中看到的, eg.com.taman.mod1 已导出 eg.com.taman.service ,所以 eg.com.taman.mod2 要求mod1导入其导出的所有包,以便在其子类型(类、枚举、接口等)中使用它们。

但是请记住, exports 关键字将包导出到其他模块,并且需要关键字 imports 模块才能在内部使用所有导出的包。在模块中定义但未导出的任何包都是私有封装的,永远无法访问。

一个模块可以有超过这个数量的元数据,但这足以让您开始模块化编程。

现在,让我们确定一些关于模块描述符语法的要点。

关于模块描述符的注意事项

================

在构建模块描述符之前,您应该记住以下要点:

  • 模块描述符可以只包含模块名而不包含其他内容;不包含导出或requires子句。

  • 模块描述符可以由一个或多个exports子句组成,而不包含requires子句;这意味着它将包导出到其他模块,但不依赖于任何其他模块—它是一个独立的模块。

  • 模块描述符可以同时具有exports和requires子句;这意味着它将包导出到其他模块并使用其他模块的包—因为它依赖于其他模块,所以它不是一个独立的模块。

  • 模块描述符可以有零个、一个或多个requires子句。

模块是从modulepath加载的(就像类是从类路径加载的一样)。

等等,你说。为什么我不能像以前那样使用类路径?

为什么选择modulepath?

====================

作为一名Java开发人员,您知道什么是类路径地狱:与Windows®编程中的DLL地狱类似,Java中的类路径地狱之所以出现,是因为您的程序不是一组固定的代码,而是JVM在特定实例中加载的一组类。您的代码可能处于这样一种情况:由于解析规则,平台上的同一命令行会导致不同的行为。目录结构可能不同。标准库的版本可以不同,也可以隐藏。因为Java支持第一次遇到的策略,所以未知的排序依赖关系可能会使代码变得混乱。

从Java9开始,您将跳入另一种地狱: modulepath地狱

类路径是用户定义并内置的一系列类和包或jar。JVM或Java编译器需要类路径来编译应用程序或类。

在Java9之前,编译器和运行时通过类路径定位类型:包含已编译Java类的文件夹和库存档文件的列表。它是由CLASSPATH环境变量、放在JRE特殊文件夹中的扩展以及提供给javac和java命令的选项的组合定义的。目标是减少应用程序启动时间。

因为类型可以从几个不同的位置加载,所以搜索这些位置的顺序会导致应用程序变得脆弱。

很多年前,我在我的系统上安装了一个来自第三方供应商的Java应用程序。该应用程序的安装程序将一个旧版本的Java库放入JRE的extensions文件夹中。我系统上的几个Java应 《一线大厂Java面试题解析+后端开发学习笔记+最新架构讲解视频+实战项目源码讲义》无偿开源 威信搜索公众号【编程进阶路】 用程序依赖于该库的更新版本,因为它包含了库的旧类型的附加类型和增强版本。

因为JRE的extensions文件夹中的类是在类路径上的其他类之前加载的,所以依赖于较新库版本的应用程序停止工作,在运行时出现 NoClassDefFoundErrors 和 NoSuchMethodErrors 失败,有时是在应用程序开始执行很久之后。

modulepath是一系列模块(以文件夹或JAR格式提供)。如果模块是文件夹格式,则表示该模块是分解模块格式。如果它是JAR格式的,那么这个JAR就是模块化JAR。

模块和模块描述符提供的可靠配置有助于消除许多此类运行时类路径问题。每个模块都显式地声明其依赖项,这些依赖项作为应用程序启动来解析。

Python入门基础知识点(模块)

常见的场景:一个模块就是一个包含了python定义和声明的文件,文件名就是模块名字加上.py的后缀。

   但其实import加载的模块分为四个通用类别: 

  1 使用python编写的代码(.py文件)

  2 已被编译为共享库或DLL的C或C++扩展

  3 包好一组模块的包

  4 使用C编写并链接到python解释器的内置模块

为何要使用模块?

   如果你退出python解释器然后重新进入,那么你之前定义的函数或者变量都将丢失,因此我们通常将程序写到文件中以便永久保存下来,需要时就通过python test.py方式去执行,此时test.py被称为脚本script。

    随着程序的发展,功能越来越多,为了方便管理,我们通常将程序分成一个个的文件,这样做程序的结构更清晰,方便管理。这时我们不仅仅可以把这些文件当做脚本去执行,还可以把他们当做模块来导入到其他的模块中,实现了功能的重复利用

序列化

什么叫序列化——将原本的字典、列表等内容转换成一个字符串的过程就叫做序列化

序列化的目的

1、以某种存储形式使自定义对象持久化;
2、将对象从一个地方传递到另一个地方。
3、使程序更具维护性

json模块:

Json模块提供了四个功能:dumps、dump、loads、load

import json
dic = {\'k1\':\'v1\',\'k2\':\'v2\',\'k3\':\'v3\'}
str_dic = json.dumps(dic)  #序列化:将一个字典转换成一个字符串
print(type(str_dic),str_dic)  #<class \'str\'> {"k3": "v3", "k1": "v1", "k2": "v2"}
#注意,json转换完的字符串类型的字典中的字符串是由""表示的

dic2 = json.loads(str_dic)  #反序列化:将一个字符串格式的字典转换成一个字典
#注意,要用json的loads功能处理的字符串类型的字典中的字符串必须由""表示
print(type(dic2),dic2)  #<class \'dict\'> {\'k1\': \'v1\', \'k2\': \'v2\', \'k3\': \'v3\'}


list_dic = [1,[\'a\',\'b\',\'c\'],3,{\'k1\':\'v1\',\'k2\':\'v2\'}]
str_dic = json.dumps(list_dic) #也可以处理嵌套的数据类型 
print(type(str_dic),str_dic) #<class \'str\'> [1, ["a", "b", "c"], 3, {"k1": "v1", "k2": "v2"}]
list_dic2 = json.loads(str_dic)
print(type(list_dic2),list_dic2) #<class \'list\'> [1, [\'a\', \'b\', \'c\'], 3, {\'k1\': \'v1\', \'k2\': \'v2\'}]

loads和dumps

dump和json方法使用:

import json
f = open(\'json_file\',\'w\')
dic = {\'k1\':\'v1\',\'k2\':\'v2\',\'k3\':\'v3\'}
json.dump(dic,f)  #dump方法接收一个文件句柄,直接将字典转换成json字符串写入文件
f.close()

f = open(\'json_file\')
dic2 = json.load(f)  #load方法接收一个文件句柄,直接将文件中的json字符串转换成数据结构返回
f.close()
print(type(dic2),dic2)

json的格式化输出:

import json
data = {\'username\':[\'李华\',\'二愣子\'],\'sex\':\'male\',\'age\':16}
json_dic2 = json.dumps(data,sort_keys=True,indent=2,separators=(\',\',\':\'),ensure_ascii=False)
print(json_dic2)

 pickle模块

用于序列化的两个模块

  • json,用于字符串 和 python数据类型间进行转换
  • pickle,用于python特有的类型 和 python的数据类型间进行转换

pickle模块提供了四个功能:dumps、dump(序列化,存)、loads(反序列化,读)、load  (不仅可以序列化字典,列表...可以把python中任意的数据类型序列化

import pickle
dic = {\'k1\':\'v1\',\'k2\':\'v2\',\'k3\':\'v3\'}
str_dic = pickle.dumps(dic)
print(str_dic)  #一串二进制内容
​
dic2 = pickle.loads(str_dic)
print(dic2)    #字典
​
import time
struct_time  = time.localtime(1000000000)
print(struct_time)
f = open(\'pickle_file\',\'wb\')
pickle.dump(struct_time,f)
f.close()
​
f = open(\'pickle_file\',\'rb\')
struct_time2 = pickle.load(f)

json是一种所有的语言都可以识别的数据结构,如果我们将一个字典或者序列化成了一个json存在文件里,那么java代码或者js代码也可以拿来用.pickle 只支持python

小结:
  son模块里的dumps是将python的数据结构转换成字符串,loads是将字符串类型转换成python的数据结构
​
    json模块里的dump是将python的数据结构转换成字符串,然后存入到文件当中
​
    json模块里的load是将文件中字符串类型转换成python的数据结构
​
    pickle模块里的dumps是将python的数据结构转换成二进制的文件,loads是将二进制的文件转换成python的
​
        数据结构
​
    pickle模块里的dump是将python的数据结构转换成二进制然后存入到文件中
​
    pickle模块里的load是将文件中的二进制文件转成python的数据结构

random 模块

random是一个随机数模块,我们一般用来生成一些没有规则的内容

获取0-1之间的随机小数

import random
print(random.random())

想要生成指定的数字内的随机小数怎么办??

import random
print(random.uniform(1,5))

.整数怎么做到随机呢?

import random
print(random.randint(1,5))

生成随机的奇数和偶数??

import random
print(random.randrange(1,5,2))   #随机生成1-5的奇数
print(random.randrange(0,5,2))   #随机生成0-5的偶数

有一个列表 lst = [\'张开\',\'宝元\',\'佩奇\',\'太白\'],随机抽一个人出来

import random
lst = [\'张开\',\'宝元\',\'佩奇\',\'太白\']
print(random.choice(lst))

同样的列表,随机抽出两个来

import random
lst = [\'张开\',\'宝元\',\'佩奇\',\'太白\']
print(random.choices(lst,k=2))    #抽取的内容会是重复的

让列表出现的两个不能是重复的

import random
lst = [\'张开\',\'宝元\',\'佩奇\',\'太白\']
print(random.sample(lst,k=2))

一个列表你给让他变成随机排序的

import random
lst = [1,2,3,4,5,6]
random.shuffle(lst)
print(lst)

课堂示例:

# 随机数
import random  # 内置的
# print(random.random())
# 0-1 之间随机小数

# print(random.randint(1,10))
# 起始位置,终止位置 两头都包含

# print(random.randrange(1,21,2))
# 起始位置,终止位置(不包含),步长

# print(random.choice([\'alex\',\'wusir\',\'eva_j\']))
# 从有序数据结构中随机选择一个

# print(random.choices([\'wusir\',\'tialaing\',\'taihei\',\'ritian\'],k=2))
# 随机选择两个,但是有重复

# print(random.sample([\'wusir\',\'tialaing\',\'taihei\',\'ritian\'],k=2))
# 随机选择两个,没有重复

# li = [1,2,3,4,6]
# random.shuffle(li)  # 洗牌 打乱顺序
# print(li)

# 随机数  验证码  微信红包

# print(chr(65),chr(90),chr(97),chr(122))

# U = chr(random.randrange(65,91))
# L = chr(random.randrange(97,123))
# n1 = random.randrange(0,10)
# n2 = random.randrange(0,10)
# print(U, L,n1,n2)

os模块

os模块是与操作系统交互的一个接口​

和文件夹相关:

os.makedirs(\'dirname1/dirname2\')    可生成多层递归目录
os.removedirs(\'dirname1\')    若目录为空,则删除,并递归到上一级目录,如若也为空,则删除,依此类推
os.mkdir(\'dirname\')    生成单级目录;相当于shell中mkdir dirname
os.rmdir(\'dirname\')    删除单级空目录,若目录不为空则无法删除,报错;相当于shell中rmdir dirname
os.listdir(\'dirname\')    列出指定目录下的所有文件和子目录,包括隐藏文件,并以列表方式打印

和文件相关:

os.remove()  删除一个文件
os.rename("oldname","newname")  重命名文件/目录
os.stat(\'path/filename\')  获取文件/目录信息

和操作系统差异相关:

os.sep    输出操作系统特定的路径分隔符,win下为"\\\\",Linux下为"/"
os.linesep    输出当前平台使用的行终止符,win下为"\\r\\n",Linux下为"\\n"
os.pathsep    输出用于分割文件路径的字符串 win下为;,Linux下为:
os.name    输出字符串指示当前使用平台。win->\'nt\'; Linux->\'posix\'

和执行系统命令相关:

os.system("bash command")  运行shell命令,直接显示
os.popen("bash command).read()  运行shell命令,获取执行结果
os.environ  获取系统环境变量

path系列,和路径相关:

os.path.abspath(path) 返回path规范化的绝对路径
os.path.split(path) 将path分割成目录和文件名二元组返回
os.path.dirname(path) 返回path的目录。其实就是os.path.split(path)的第一个元素
os.path.basename(path) 返回path最后的文件名。如何path以/或\\结尾,那么就会返回空值,即os.path.split(path)的第二个元素。
os.path.exists(path)  如果path存在,返回True;如果path不存在,返回False
os.path.isabs(path)  如果path是绝对路径,返回True
os.path.isfile(path)  如果path是一个存在的文件,返回True。否则返回False
os.path.isdir(path)  如果path是一个存在的目录,则返回True。否则返回False
os.path.join(path1[, path2[, ...]])  将多个路径组合后返回,第一个绝对路径之前的参数将被忽略
os.path.getatime(path)  返回path所指向的文件或者目录的最后访问时间
os.path.getmtime(path)  返回path所指向的文件或者目录的最后修改时间
os.path.getsize(path) 返回path的大小<br></em>


os.getcwd() 获取当前工作目录,即当前python脚本工作的目录路径
os.chdir("dirname")  改变当前脚本工作目录;相当于shell下cd
os.curdir  返回当前目录: (\'.\')
os.pardir  获取当前目录的父目录字符串名:(\'..\')

注意:os.stat(\'path/filename\') 获取文件/目录信息 的结构说明:

stat 结构:
st_mode: inode 保护模式
st_ino: inode 节点号。
st_dev: inode 驻留的设备。
st_nlink: inode 的链接数。
st_uid: 所有者的用户ID。
st_gid: 所有者的组ID。
st_size: 普通文件以字节为单位的大小;包含等待某些特殊文件的数据。
st_atime: 上次访问的时间。
st_mtime: 最后一次修改的时间。
st_ctime: 由操作系统报告的"ctime"。在某些系统上(如Unix)是最新的元数据更改的时间,在其它系统上(如Windows)是创建时间(详细信息参见平台的文档)。 

课堂整体讲解,着重点:

# 1.os模块和当前电脑操作系统做交互
# os 内置模块

# 文件夹:
import os
# os.makedirs(\'app/a/b/c\')   # 递归创建文件       ***
# os.removedirs(\'app/a/b/c\') # 递归删除文件, 当这个要删除的目录有文件就停止删除  ***
# os.mkdir(\'app\')   # 创建单个目录                ***
# os.rmdir(\'app\')     # 删除单个目录,目录如果有东西就报错不进行删除    ***
# print(os.listdir(\'D:\\PyCharmProject\\s20\\day14\'))    ****
# 查看某个目录下的内容

# 文件:
# os.remove() #删除这个文件,删除了就不能恢复了    *****
# os.rename()   #修改文件的名字                  *****

# 操作系统:
# print(repr(os.sep))  #\\\\
# C:\\Users\\lucky\\Documents\\Tencent Files\\932023756\\FileRecv
# 路径符号

# print(repr(os.linesep))
# 换行符

# print(repr(os.pathsep))
# 环境变量的分割

# print(repr(os.name))

# print(os.system(\'dir\'))

# print(os.popen(\'dir\').read())               ***
# 在写程序的时候可以下发一些操作系统的指令
# 在linux系统上相当于发shell命令

# print(os.environ) 查看 高级 -- 环境变量 -- path   *

# 路径:
# print(os.path.abspath(\'b\'))      ****
# 获取当前文件的绝对路径
# print(os.path.split(r\'D:\\PyCharmProject\\s20\\day14\\b\'))
# 将这个文件的绝对路径分成目录和文件
# print(os.path.basename(r\'D:\\PyCharmProject\\s20\\day14\\b\'))  **
# 获取的是这个文件的名字
# print(os.path.dirname(r\'D:\\PyCharmProject\\s20\\day14\\b\'))   ***
# 获取的是这个文件的目录路径
# print(os.path.exists(r\'D:\\PyCharmProject\\s20\\day10\\07 今日总结.py\')) **
# 判断这个文件是否存在
# print(os.path.isabs(r\'D:\\PyCharmProject\\s20\\day14\\b\'))
# 判断是否是绝对路径

# print(os.path.join(\'C:\\\\\',\'app\',\'a\',\'b\'))                 *****
# #路径拼接的  软件开发规范 框架

# os.stat()

# print(os.stat(\'b\').st_size)   #获取文件大小 ,坑目录        ***
# print(os.getcwd())  # 获取工作路劲                         ***

# os.chdir(\'D:\\PyCharmProject\\s20\\day13\')  # 路劲切换        *
# print(os.getcwd())

sys模块

sys模块是与python解释器交互的一个接口

sys.argv           命令行参数List,第一个元素是程序本身路径
sys.exit(n)        退出程序,正常退出时exit(0),错误退出sys.exit(1)
sys.version        获取Python解释程序的版本信息
sys.path           返回模块的搜索路径,初始化时使用PYTHONPATH环境变量的值
sys.platform       返回操作系统平台名称

着重点:

# 1. sys模块 python解释器交互的

# sys 内置的

import sys

# print(sys.argv[-1])       ***
# 接受cmd方式调用 后边的参数会传递进来
# linux系统上  -- 后端开发 -- 数据库(文件) ip + 端口

# print(sys.path)   # 添加自定义模块路径的                 # ******

# print(sys.version)  # 版本  获取解释的版本号

# sys.platform = \'win1988\'

# print(sys.platform)   #获取当前操作系统的平台位数          # 不是定死的

# print(sys.exit(1))

hashlib模块

算法介绍 Python的hashlib提供了常见的摘要算法,如MD5,SHA1等等。

什么是摘要算法呢?摘要算法又称哈希算法、散列算法。它通过一个函数,把任意长度的数据转换为一个长度固定的数据串(通常用16进制的字符串表示)。

摘要算法就是通过摘要函数f()对任意长度的数据data计算出固定长度的摘要digest,目的是为了发现原始数据是否被人篡改过。

摘要算法之所以能指出数据是否被篡改过,就是因为摘要函数是一个单向函数,计算f(data)很容易,但通过digest反推data却非常困难。而且,对原始数据做一个bit的修改,都会导致计算出的摘要完全不同。

我们以常见的摘要算法MD5为例,计算出一个字符串的MD5值:

import hashlib
 
md5 = hashlib.md5()
md5.update(\'how\'.encode(\'utf-8\'))
print(md5.hexdigest())
 
结果:db88a0257c220dbfdd2e40f6152d6a8d

如果数据量很大,可以分块多次调用update(),最后计算的结果是一样的:

import hashlib
md5 = hashlib.md5()
md5.update(\'how你好啊,我不好\'.encode(\'utf-8\'))
print(md5.hexdigest())
结果:3bfa4bfb7b59a59f27c24ebdd89cd13c
 
 
import hashlib
md5 = hashlib.md5()
md5.update(\'how\'.encode(\'utf-8\'))
md5.update(\'你好啊,我不好\'.encode(\'utf-8\'))
print(md5.hexdigest())
结果:3bfa4bfb7b59a59f27c24ebdd89cd13c

MD5是最常见的摘要算法,速度很快,生成结果是固定的128 bit字节,通常用一个32位的16进制字符串表示。另一种常见的摘要算法是SHA1,调用SHA1和调用MD5完全类似:

import hashlib
 
sha1 = hashlib.sha1()
sha1.update(\'how to use sha1 in\'.encode(\'utf-8\'))
sha1.update(\'python hashlib?\'.encode(\'utf-8\'))
print(sha1.hexdigest())
 
结果:86e1eae2a08c152d39b55baed085c71a0cc9d10b

SHA1的结果是160 bit字节,通常用一个40位的16进制字符串表示。

SHA1的安全系数比MD5还要高一些,而且摘要的长度要比MD5长一些.

比SHA1更安全的算法是SHA256和SHA512,不过越安全的算法越慢,而且摘要长度更长。

网站运维人员是可以访问数据库的,也就是能获取到所有用户的口令。正确的保存口令的方式是不存储用户的明文口令,而是存储用户口令的摘要,比如MD5:

username | password
---------+---------------------------------
michael  | e10adc3949ba59abbe56e057f20f883e
bob      | 878ef96e86145580c38c87f0410ad153
alice    | 99b1c2188db85afee403b1536010c2c9

考虑这么个情况,很多用户喜欢用123456,888888,password这些简单的口令,于是,黑客可以事先计算出这些常用口令的MD5值,得到一个反推表:

\'e10adc3949ba59abbe56e057f20f883e\': \'123456\'
\'21218cca77804d2ba1922c33e0151105\': \'888888\'
\'5f4dcc3b5aa765d61d8327deb882cf99\': \'password\'

这样,无需破解,只需要对比数据库的MD5,黑客就获得了使用常用口令的用户账号。

对于用户来讲,当然不要使用过于简单的口令。但是,我们能否在程序设计上对简单口令加强保护呢?

由于常用口令的MD5值很容易被计算出来,所以,要确保存储的用户口令不是那些已经被计算出来的常用口令的MD5,这一方法通过对原始口令加一个复杂字符串来实现,俗称“加盐”:

import hashlib
md5 = hashlib.md5(\'salt\'.encode(\'utf-8\'))  #这是加盐了的
md5.update(\'how你好啊,我不好\'.encode(\'utf-8\'))
print(md5.hexdigest()) 
#结果 49f3d086ef5582b12d294c6f1b1c9dea
 
 
import hashlib
md5 = hashlib.md5()
md5.update(\'how你好啊,我不好\'.encode(\'utf-8\'))
print(md5.hexdigest()) 
#结果 3bfa4bfb7b59a59f27c24ebdd89cd13c

经过Salt处理的MD5口令,只要Salt不被黑客知道,即使用户输入简单口令,也很难通过MD5反推明文口令。

但是如果有两个用户都使用了相同的简单口令比如123456,在数据库中,将存储两条相同的MD5值,这说明这两个用户的口令是一样的。有没有办法让使用相同口令的用户存储不同的MD5呢?

如果假定用户无法修改登录名,就可以通过把登录名作为Salt的一部分来计算MD5,从而实现相同口令的用户也存储不同的MD5。

摘要算法在很多地方都有广泛的应用。要注意摘要算法不是加密算法,不能用于加密(因为无法通过摘要反推明文),只能用于防篡改,但是它的单向计算特性决定了可以在不存储明文口令的情况下验证用户口令。

 课堂实例:

# 1.# 加密算法
# 作用: 当做密码
# 判断一致性
# 2.加密后不可逆 不能解 (一年前暴力破解 -- 撞库)
# (现在md5 反推)

# 3.sha1,sha256,sha512

# alex3714
# **********

# import hashlib
# md5 = hashlib.md5(\'盐\'.encode(\'utf-8\'))  # 选择加密方式  加盐
# md5.update(\'alex3714\'.encode(\'utf-8\')) # 将明文转成字节然后进行加密
# print(md5.hexdigest()) # 生成密文

# md5 = hashlib.md5()  # 选择加密方式  加盐
# md5.update(\'alex3714\'.encode(\'utf-8\')) # 将明文转成字节然后进行加密
# print(md5.hexdigest()) # 生成密文

# sha512 = hashlib.sha512()
# sha512.update(\'alex3714\'.encode(\'utf-8\'))
# print(sha512.hexdigest())


# # sha512
# 优点:
#     安全
# 缺点:
#
#
# # md5
# 优点:
#     安全,快
# 缺点:
#     容易破解


# user,pwd = input(\'user|pwd:\').strip().split(\'|\')
# import hashlib
#
# md5 = hashlib.md5(str(user).encode(\'utf-8\'))
# md5.update(pwd.encode(\'utf-8\'))
# print(md5.hexdigest())

# md5,sha1,sha256,sha512

 

shelve模块

课堂示例:

# 序列化:
# 1.文件 + 字典操作

# import shelve
# f = shelve.open(\'c\',writeback=True)  #创建文件  # writeback = True 回写

# f[\'name\'] = \'alex\'
#
# f[\'age\'] = 18
#
# print(f[\'name\'],f[\'age\'])

# f[\'name\'] = [\'alex\',\'wusir\']
#
# print(f[\'name\'])

# print(f)  # 这样是查看不了字典

# for i in f:
#     print(i)   #获取到所有的键

# print(f.keys())  #keys也不行

# for i in f:
#     print(f[i])

# dump load   咱们不用写,自动帮咱们写的

# f[\'name\'] = {\'2\':4}

# print(f[\'name\'])

# for i in f:
#     print(i)

# 注意:以后你们会出现一个问题,咱们对字典的操作内容,有时候写不进去.在open

# print(f[\'name\'])

# f[\'name\'] = \'wusir\'
# print(f[\'name\'])   # {\'2\':4}

# f[\'name\'] = [1,2,34]

# print(list(f.keys()))


# json 最常用,前后端分离的时候数据交互  前后端分离  必回
# pickle python 不怎么常用   河南方言             必了解
# shelve 建议使用它,它简单   重庆,东北,容易学      必了解

今日总结:

# 1.序列化
#     json   # 必会

            # dump   把字典转成字符串存入文件
            # load   将文件的字符串转成字典
            # dumps  将字典转成字符串
            # loads  将字符串转成字典

#     pickle # 了解
#     shevle # 了解

# 2. 随机数
#     random.random()  0-1 之间的小数
#     验证码
#     random.randint(1,10)

# 3. os 操作系统

#     路径部分
#     os.path.join
#     os.path.abspath
#     os.path.basename
#     os.path.dirname
#     os.path.getsize()  # 获取大小
#     os.remove()
#     os.rename()
#     os.listdir()
#     os.chdir()  # 切换目录
#     os.makedirs(\'app/a/b\') 递归创建文件夹
#     os.removedirs(\'app/a/b\')  递归删除
#     os.mkdir()  创建单个文件夹
#     os.rmdir()  删除单个文件夹

# sys python解释器

# sys.argv()  在cmd中执行 可以将文件 后的内容传递到文件中使用
# sys.path    python解释器加载的路径,自定义模块添加到这里
# sys.exit()
# sys.version()  获取解释器的版本号
# sys.platform   获取当前平台的位数


# hashlib 加密算法
# md5,sha1,sha256,sha512
# 1.先导入模块
# 2.创建一个加密方式
# 3.将要加密的内容编码成字节后加密
# 4.生成密文

# import hashlib
# md5 = hashlib.md5(b\'alex\')
# md5.update(\'alex3714\'.encode(\'utf-8\'))
# print(md5.hexdigest())

 

以上是关于JDK9模块化知识和规则入门的主要内容,如果未能解决你的问题,请参考以下文章

分析C++软件异常需要掌握的汇编知识汇总(实战经验分享)

译 13简单的优秀编码规则(从我15年的经验)

java入门经验分享——记面向对象先导课程学习感想

八年Java开发经验大佬,分享各专题面试文档及架构知识!

经验分享一文带你了解编程入门的通识知识

Rust语言入门关键技术与实战经验