首页 文章 精选 留言 我的

精选列表

搜索[天玑9500],共10000篇文章
优秀的个人博客,低调大师

天不生笛卡儿,万古如长夜

靠什么,可以从数据库筛选记录? 靠什么,人工神经网络识别不同的数字和字符? 靠什么,甚至量子力学也可以使用? 靠什么,动词算子式代码生成器可以拥有强大的变形能力? 这一切都是笛卡尔积! 最新的动词算子式代码生成器的理论视频已发布,请看 https://www.bilibili.com/video/BV1br421G7uz/ 此视频详细介绍了笛卡尔积在向量,矩阵,人工神经网络,数据库和动词算子式代码生成器中的应用。 并对动词算子ListAll和Assign进行了代码讲解。深入浅出的介绍了通用代码生成器的基础理论。欢迎大家观看。

优秀的个人博客,低调大师

天呐,我居然可以隔空作画了

摘要:本案例将使用 YOLOX、 SCNet两个模型,实现一个简单的隔空作画趣味应用 本文分享自华为云社区《ModelBox开发案例 - 隔空作画》,作者:吴小鱼。 本案例将使用YOLOX、SCNet两个模型,实现一个简单的隔空作画趣味应用,最终效果如下所示: 案例所需资源(代码、模型、测试数据等)均可从obs桶下载。 模型训练 我们使用面向开发者的一站式AI开发平台ModelArts进行模型的训练: ModelArts提供了包括数据标注,训练环境,预置算法在内的丰富的功能,甚至可以通过订阅预置算法实现0代码的模型训练工作。当然你也可以在本地训练自己的模型。我们假设你现在已经拥有了训练好的模型,接下来我们需要将训练好的模型转换成为可以在开发板上运行的模型。 模型转换 我们发布了开发板模型转换案例,参见RK3568模型转换验证案例: 在这个案例中我们演示了从环境适配到模型的转换验证的全流程样例代码,开发者可以通过“Run in ModelArts”一键将Notebook案例在ModelArts控制台快速打开、运行以及进行二次开发等操作。 开发环境部署 使用开发板进行ModelBox AI应用开发有两种方式,一是开发板连接显示器和键盘鼠标,安装Ubuntu桌面,直接在开发板上进行开发;二是使用远程连接工具(如VS Code中的Remote-SSH)从PC端登录开发板进行开发。这里我们推荐第二种方式,因为PC端可以使用功能更丰富、界面更友好的IDE。 1.配置网络 PC连接开发板需要知道开发板的ip,但是开发板默认没有固定ip,我们提供了ModelBox PC Tool,可以自动为开发板配置ip,也可以在推理阶段很方便的进行视频推流拉流。 PC Tool位于SDK的connect_wizard目录中: 双击connect_wizard.exe,在页面中可以看到有两种开发板连接方式,我们使用网线连接开发板的方式: 按照指引断开或连接网线: 等待一小段时间,可以看到来到了第三步,此时开发板已设置为默认ip:192.168.2.111,PC使用该ip即可SSH登录: 2. 远程连接开发板 我们推荐在PC端使用VS Code远程连接开发板来对设备操作。 使用VS Code连接开发板可以参考我们发布的ModelBox 端云协同AI开发套件(RK3568)上手指南。同时,上手指南也介绍了如何将开发板注册到HiLens管理控制台进行更方便的在线管理。 应用开发 接下来我们会以隔空作画demo为例,介绍如何使用ModelBox开发一个AI应用。 1.创建工程 SDK提供了工程脚本create.py,可以使用./create.py -h查看脚本帮助: ModelBox提供了可视化图编排工具:Editor,可以使用./create.py -t editor开启图编排服务: 服务默认ip即为192.168.2.111,如需配置其他ip或端口,可以通过-i ip:port参数进行配置。 点击链接即可进入可视化编辑界面,我们点击编排进入工程开发界面,如果进一步了解ModelBox相关内容,可以点击右上角帮助: 进入编排界面,点击右上角新建项目: 项目路径填写workspace,项目名称填写hand_painting, 确认: 可以看到我们已经拥有了一个带有http收发单元的默认图: 其中,区域1为SDK预置的高性能通用流单元,区域2为可视化编排界面,区域3为对应的图配置文件内容。同时,VS Code对应目录下也出现了hand_painting项目: 2.创建推理功能单元 接下来,我们创建推理流单元: 对于手检测模型,我们将流单元命名为hand_detection,模型文件名即为转换好的检测模型名:yolox_hand.rknn,此模型输入为image,输出为feature map,所以我们添加int类型的输入端口与float类型的输出端口。关于开发板的推理流单元创建,在处理类型时我们选择cuda,即为npu推理,推理引擎可选任意一款,目前开发板SDK可以自动进行识别转换。最后将功能单元分组修改为inference,点击确认,即可看到,在右侧inference页签下出现了: 同时,在VS Code工程model目录下可以看到创建好的推理流单元: 同样的,我们创建pose_detection推理流单元: 3.创建后处理功能单元 除了推理流单元外,隔空作画demo还需要一些通用功能单元:检测后处理单元、感兴趣区域提取单元、作画单元,我们新建三个python功能单元来满足上述需求。 对于检测后处理单元,我们希望通过原图和hand_detection的输出解码出手检测框,所以该单元应该有两个输入。此外,对于画幅中有手或者没有检测到手两种状态,我们希望该功能单元分情况处理,检测到手时,将检测结果送入感兴趣区域提取单元,没有检测到手时,直接返回,因此功能单元类型选择:IF_ELSE。新建单元如下: 同样的,根据输入输出与功能单元状态,我们创建extract_roi和painting两个功能单元: 4.流程图编排 拖拽 需要的功能单元全部创建好后,我们可以着手编排流程图,我们编排一个视频处理的图,暂时不需要http收发单元,可以删除不需要的单元: 在Generic列表下将虚拟输入单元input和我们刚刚创建的三个功能单元拖入画布: 在Image列表下将模型推理需要用到的预处理单元resize拖入画布,因为我们需要两个resize单元,所以重复拖入: 值得注意的是,resize单元需要配置参数,需要点击该单元进行配置: 在Input列表下拖入输入解析单元data_source_parser: 在Video列表下拖入视频处理需要的单元video_demuxer、video_decoder、video_out: 最后,在Inference列表下拖入我们创建的两个推理单元: 编排 将功能单元按照处理逻辑进行连接:虚拟输入input连接输入解析data_source_parser,解析后送入视频解包与解码单元: 解码输出送入预处理后可直接进行推理: 推理后处理需要输入原图与推理结果,没有结果则直接连接视频输入单元,有结果则连接感兴趣区域提取单元: 提取结果送入预处理与推理: 最后,得到的关键点结果与原图送入作画单元,作画结果送入视频输出单元进行保存: 这样,我们就完成了流程图的编排,可以看到在GraphViz区域也出现了完整的图表述: 保存项目,转到VS Code进行每个单元的代码实现: 5.代码补全 可视化编排中,创建的推理单元位于项目的model目录下,通用单元位于etc/flowunit目录下,流程图位于graph目录下,可以看到创建的单元与图都已同步过来: 其中,video_decoder需要指定类型: video_decoder7 [ type=flowunit flowunit=video_decoder device=rknpu, deviceid="0", pix_fmt=bgr label="{{<in_video_packet> in_video_packet}|video_decoder7|{<out_video_frame> out_video_frame}}" ] 推理单元 首先完善推理单元,对于推理功能单元,只需要提供独立的toml配置文件,指定推理功能单元的基本属性即可,目录结构为: [flowunit-name] |---[flowunit-name].toml #推理功能单元配置 |---[model].rknn #模型文件 |---[infer-plugin].so #推理自定义插件 ModelBox框架在初始化时,会扫描目录中的toml后缀的文件,并读取相关的推理功能单元信息。[infer-plugin].so是推理所需插件,推理功能单元支持加载自定义插件,开发者可以实现自定义算子。 将模型拷入对应文件夹,以hand_detection为例我们看一下推理功能单元配置文件: 配置文件中有一些单元类型、模型名称、输入输出的基本配置,可以酌情修改。 通用单元 Python通用单元需要提供独立的toml配置文件,指定python功能单元的基本属性。一般情况,目录结构为: [FlowUnitName] |---[FlowUnitName].toml |---[FlowUnitName].py |---xxx.py 相较于推理单元而言,通用单元不但有配置文件,还需要完善具体的功能代码,以yolox_post为例,首先是功能单元配置文件: # Copyright (c) Huawei Technologies Co., Ltd. 2022. All rights reserved. # Basic config [base] name = "yolox_post" # The FlowUnit name device = "cpu" # The flowunit runs on cpu version = "1.0.0" # The version of the flowunit type = "python" # Fixed value, do not change description = "description" # The description of the flowunit entry = "yolox_post@yolox_postFlowUnit" # Python flowunit entry function group_type = "generic" # flowunit group attribution, change as input/output/image ... # Flowunit Type stream = false # Whether the flowunit is a stream flowunit condition = true # Whether the flowunit is a condition flowunit collapse = false # Whether the flowunit is a collapse flowunit collapse_all = false # Whether the flowunit will collapse all the data expand = false # Whether the flowunit is a expand flowunit # The default Flowunit config [config] item = "value" # Input ports description [input] [input.input1] # Input port number, the format is input.input[N] name = "in_image" # Input port name type = "uint8" # input port data type ,e.g. float or uint8 device = "cpu" # input buffer type [input.input2] # Input port number, the format is input.input[N] name = "in_feat" # Input port name type = "uint8" # input port data type ,e.g. float or uint8 device = "cpu" # input buffer type # Output ports description [output] [output.output1] # Output port number, the format is output.output[N] name = "has_hand" # Output port name type = "float" # output port data type ,e.g. float or uint8 [output.output2] # Output port number, the format is output.output[N] name = "no_hand" # Output port name type = "float" # output port data type ,e.g. float or uint8 Basic config是一些单元名等基本配置,Flowunit Type是功能单元类型,yolox_post是一个条件单元,所以可以看到condition为true,此外还有一些展开、归拢等性质,可以在AI Gallery ModelBox)板块下看到更多案例。 config为单元需要配置的一些属性,如本单元需要一些特征图size、阈值等信息,所以在配置文件中修改config为: [config] net_h = 320 net_w = 320 num_classes = 2 conf_threshold = 0.5 iou_threshold = 0.5 此外,输入输出type根据实际逻辑可能进行一些修改: # Input ports description [input] [input.input1] # Input port number, the format is input.input[N] name = "in_image" # Input port name type = "uint8" # input port data type ,e.g. float or uint8 device = "cpu" # input buffer type [input.input2] # Input port number, the format is input.input[N] name = "in_feat" # Input port name type = "float" # input port data type ,e.g. float or uint8 device = "cpu" # input buffer type # Output ports description [output] [output.output1] # Output port number, the format is output.output[N] name = "has_hand" # Output port name type = "uint8" # output port data type ,e.g. float or uint8 [output.output2] # Output port number, the format is output.output[N] name = "no_hand" # Output port name type = "uint8" # output port data type ,e.g. float or uint8 接下来,我们查看yolox_post.py,可以看到创建单元时已经生成了基本接口: # Copyright (c) Huawei Technologies Co., Ltd. 2022. All rights reserved. #!/usr/bin/env python # -*- coding: utf-8 -*- import _flowunit as modelbox class yolox_postFlowUnit(modelbox.FlowUnit): # Derived from modelbox.FlowUnit def __init__(self): super().__init__() def open(self, config): # Open the flowunit to obtain configuration information return modelbox.Status.StatusCode.STATUS_SUCCESS def process(self, data_context): # Process the data in_data = data_context.input("in_1") out_data = data_context.output("out_1") # yolox_post process code. # Remove the following code and add your own code here. for buffer in in_data: response = "Hello World " + buffer.as_object() result = response.encode('utf-8').strip() add_buffer = modelbox.Buffer(self.get_bind_device(), result) out_data.push_back(add_buffer) return modelbox.Status.StatusCode.STATUS_SUCCESS def close(self): # Close the flowunit return modelbox.Status() def data_pre(self, data_context): # Before streaming data starts return modelbox.Status() def data_post(self, data_context): # After streaming data ends return modelbox.Status() def data_group_pre(self, data_context): # Before all streaming data starts return modelbox.Status() def data_group_post(self, data_context): # After all streaming data ends return modelbox.Status() 如果功能单元的工作模式是stream = false时,功能单元会调用open、process、close接口;如果功能单元的工作模式是stream = true时,功能单元会调用open、data_group_pre、data_pre、process、data_post、data_group_post、close接口;用户可根据实际需求实现对应接口。 根据单元性质,我们主要需要完善open、process接口: import _flowunit as modelbox import numpy as np from yolox_utils import postprocess, expand_bboxes_with_filter, draw_color_palette class yolox_postFlowUnit(modelbox.FlowUnit): # Derived from modelbox.FlowUnit def __init__(self): super().__init__() def open(self, config): self.net_h = config.get_int('net_h', 320) self.net_w = config.get_int('net_w', 320) self.num_classes = config.get_int('num_classes', 2) self.num_grids = int((self.net_h / 32) * (self.net_w / 32)) * (1 + 2*2 + 4*4) self.conf_thre = config.get_float('conf_threshold', 0.3) self.nms_thre = config.get_float('iou_threshold', 0.4) return modelbox.Status.StatusCode.STATUS_SUCCESS def process(self, data_context): modelbox.info("YOLOX POST") in_image = data_context.input("in_image") in_feat = data_context.input("in_feat") has_hand = data_context.output("has_hand") no_hand = data_context.output("no_hand") for buffer_img, buffer_feat in zip(in_image, in_feat): width = buffer_img.get('width') height = buffer_img.get('height') channel = buffer_img.get('channel') img_data = np.array(buffer_img.as_object(), copy=False) img_data = img_data.reshape((height, width, channel)) feat_data = np.array(buffer_feat.as_object(), copy=False) feat_data = feat_data.reshape((self.num_grids, self.num_classes + 5)) ratio = (self.net_h / height, self.net_w / width) bboxes = postprocess(feat_data, (self.net_h, self.net_w), self.conf_thre, self.nms_thre, ratio) box = expand_bboxes_with_filter(bboxes, width, height) if box: buffer_img.set("bboxes", box) has_hand.push_back(buffer_img) else: draw_color_palette(img_data) img_buffer = modelbox.Buffer(self.get_bind_device(), img_data) img_buffer.copy_meta(buffer_img) no_hand.push_back(img_buffer) return modelbox.Status.StatusCode.STATUS_SUCCESS def close(self): # Close the flowunit return modelbox.Status() 可以看到,在open中我们进行了一些参数获取,process进行逻辑处理,输入输出可以通过data_context来获取,值得注意的是输出时我们返回的是图,在检测到手时为图附加了检测框信息,该信息可以被下一单元获取。 同样的,完善其余通用功能单元,具体可以参考我们提供的代码。 应用运行 我们需要准备一个mp4文件拷贝到data文件夹下,我们提供了测试视频hand.mp4,然后打开工程目录下bin/mock_task.toml文件,修改其中的任务输入和任务输出配置为如下内容: # 任务输入,mock模拟目前仅支持一路rtsp或者本地url # rtsp摄像头,type = "rtsp", url里面写入rtsp地址 # 其它用"url",比如可以是本地文件地址, 或者httpserver的地址,(摄像头 url = "0") [input] type = "url" url = "../data/hand.mp4" # 任务输出,目前仅支持"webhook", 和本地输出"local"(输出到屏幕,url="0", 输出到rtsp,填写rtsp地址) # (local 还可以输出到本地文件,这个时候注意,文件可以是相对路径,是相对这个mock_task.toml文件本身) [output] type = "local" url = "../hilens_data_dir/paint.mp4" 配置好后在工程路径下执行build_project.sh进行工程构建: rock@rock-3a:~/lxy/examples$ cd workspace/hand_painting/ rock@rock-3a:~/lxy/examples/workspace/hand_painting$ ./build_project.sh dos2unix: converting file /home/rock/lxy/examples/workspace/hand_painting/graph/hand_painting.toml to Unix format... dos2unix: converting file /home/rock/lxy/examples/workspace/hand_painting/graph/modelbox.conf to Unix format... dos2unix: converting file /home/rock/lxy/examples/workspace/hand_painting/etc/flowunit/extract_roi/extract_roi.toml to Unix format... dos2unix: converting file /home/rock/lxy/examples/workspace/hand_painting/etc/flowunit/painting/painting.toml to Unix format... dos2unix: converting file /home/rock/lxy/examples/workspace/hand_painting/etc/flowunit/yolox_post/yolox_post.toml to Unix format... dos2unix: converting file /home/rock/lxy/examples/workspace/hand_painting/model/hand_detection/hand_detection.toml to Unix format... dos2unix: converting file /home/rock/lxy/examples/workspace/hand_painting/model/pose_detection/pose_detection.toml to Unix format... dos2unix: converting file /home/rock/lxy/examples/workspace/hand_painting/bin/mock_task.toml to Unix format... build success: you can run main.sh in ./bin folder rock@rock-3a:~/lxy/examples/workspace/hand_painting$ 构建完成后运行项目: rock@rock-3a:~/lxy/examples/workspace/hand_painting$ ./bin/main.sh 等待稍许即可以在hilens_data_dir文件夹下看到运行结果: 除了mp4外我们也支持很多其他类型的输入输出,ModelBox PC TOOL也提供了推流与拉流功能,选择输入实时视频流,启动: 运行程序时配置输出地址为推流地址,即可在本机网页中查看到运行结果: 如果需要对应用进行性能评估,只需要在流程图配置文件中开启profile: [profile] profile=true # 启用profile trace=true # 启用traceing dir="/tmp/modelbox/perf" # 设置跟踪文件路径 配置启动后,启动运行流程图,profile会每隔60s记录一次统计信息,trace会在任务执行过程中和结束时,输出统计信息。 运行流程图后,会生成性能相关的json文件,通过将json文件加载到浏览器中即可查看timeline信息。 打开chrome浏览器。 浏览器中输入chrome://tracing/。 点击界面中的Load按钮,加载trace的json文件。 加载成功后,将看到类似下面的timeline视图: 打包部署 打包 调试完成后,同样可以通过create.py脚本将应用打包发布: ./create.py -t rpm -n hand_painting 控制台中输出: sdk version is modelbox-rk-aarch64-1.0.8.8 call mb-pkg-tool pack [folder] > [rpm file] to building rpm, waiting... success: create hand_painting.rpm in /home/rock/lxy/examples/workspace/hand_painting 等待稍许,可以看到项目工程下已经生成了rpm文件夹和打包好的应用: 部署 将打包好的应用上传至华为云账号下的obs桶中: 在专业版设备管理中找到开发板,点击创建部署: 技能包选择刚刚上传的应用: 如果需要,可以配置一些启动参数,否则默认完成即可: 这样我们就已经完成了一个AI应用,从模型训练到转换到开发到部署的全部流程。关于ModelBox核心概念、功能单元和流程图开发的更多介绍,可查看ModelBox手册。 点击关注,第一时间了解华为云新鲜技术~

优秀的个人博客,低调大师

第三天学习Java:运算符

一、运算符 运算符是一种特殊的符号,用以表示数据的运算、赋值和比较等。 算法运算符 正号、负号、加减乘除、取模(取余)++自增(前),先运算后取值。++自增(后),先取值后运算。--自减(前),先运算后取值。--自减(后),先取值后运算。 ,字符串连接。%取模(取余),余数符号跟被除数相同。 小程序:class Test{ public static void main(String[] args){ int i1 = 12; int i2 = 5; double d1; double d2; d1=i1 / i2; d2=i1 / (i2+0.0); System.out.println(d1); System.out.println(d2);System.out.println("i1 % i2=" + i1 % i2);int i3 = 10;int t1 = ++i3;System.out.println("i3 = " + i3 + ",t1 = " +t1); int i4 = 10;int t2 = i4 ++;System.out.println("i4 = " + i4 + ",t2 = " + t2);int i5 = 187;System.out.println("百位数是" + i5 / 100);System.out.println("十位数是" + i5 % 100 / 10);System.out.println("个位数是" + i5 % 10);}}$ javac Test.java$ java Test2.02.4i1 % i2=2i3 = 11,t1 = 11i4 = 11,t2 = 10百位数是1十位数是8个位数是7 赋值运算符 符号:=当“=”两侧数据类型不一致时,可以使用自动类型转换或使用强制类型转换原则进行处理。支持连续赋值扩展赋值运算符:+=,-+,*=,/=,%=通常写法:i2=j2=10;i3=10,j3=20;int num1=10;num1+=2; //12int num2 = 12;num2 % =5;//2short s1=10;s1 + = 2;//不会改变变量本身数据类型s1 = s1 + 2;//报错 比较运算符(关系运算符) 运算符 运算 范例 结果== 相等于 4==3 false!= 不等于 4!=3 true< 小于 4<3 falseØ 大于 4>3 true<= 小于等于 4<=3 false = 大于等于 4>=3 true instanceof 检查是否是类的对象 “Hello” instaceof String true比较运算符的结果都是boolean型,要么是true,要么是false比较运算符“==”不能误写成“=” 逻辑运算符 & 逻辑与 | 逻辑或 ! 逻辑非&& 短路与 || 短路或 ^ 逻辑异或123a b a&b a&&b a|b a||b !a a^btrue ture true true true true false falsetrue false fasle fasle true true false truefasle true false false true true true truefasle false false false fasle false true false&与&&区分相同点:运算结果相同;当符号左边是true时,二者都会执行右边的运算。不同点:当符号左边是false时,&继续执行符号右边的运算,&&不再执行右边的运算。同理|与||,当符号左边为true时,|继续执行右边的运算,||不再执行右边的运算。开发中推荐使用||例子class Luoji{public static void main(String[] args){boolean b1 = false;int i1 = 10;if(b1 & (i1++ > 0)){System.out.println("我现在在南京");} else{System.out.println("我现在在北京");} System.out.println(i1); boolean b2 = false;int i2 = 10;if(b2 && (i2++ >0)){ System.out.println("我现在在南京");} else{ System.out.println("我现在在北京");}System.out.println(i2);}}$ javac Luoji.java$ java Luoji我现在在北京11我现在在北京10 位运算符(用的很少,二进制) 操作的都是整型变量,在一定范围内每左移一位相当于*2,右移相当于/2面试题:最高效方式计算2*8? 2<<3或8<<1运算符 运算 范例<< 左移(2) 3<<2=12 32*2=12 右移(/2) 3>>1=1 3/2=1 无符号右移 3>>>1=1 3/2=1 & 与运算符 6&3=2,转换2进制,01计算| 或运算符 6|3=7,同上^ 异或运算 6^3=5,同上~ 取反运算 ~6=-7,包括符号位全部取反 三元运算符 格式:条件表达式?表达式1:表达式2条件表达式为true,运算结果是表达式1;false为表达式2表达式1和2为同种类型。三元运算符可以嵌套使用三元运算符与if-else的联系和区别1、三元运算符可以简化if-else语句2、三元运算符要求必须返回一个结果3、if后的代码块可以有多个语句。条件表达式为boolean类型,根据条件表达式决定执行1或者2.例子,3个数取最大值:$ vim SanYuan.javaclass SanYuan{public static void main(String[] args){int n1=12;int n2=30;int n3=-43;int max=((n1>n2?n1:n2)>n3)?(n1>n2?n1:n2):n3;System.out.println("The max of n1 n2 n3 is " + max);}}$ javac SanYuan.java$ java SanYuanThe max of n1 n2 n3 is 30

优秀的个人博客,低调大师

安全运营的“天坑”? SIEM 的残酷真相

【金融特辑】光大银行科技部DBA女神带你从0到1揭秘MGR 2020年网络安全的焦点是“人的因素”,重心是“安全运营”,但是安全运营的发动机——SIEM,却始终是CISO头上的一个大包。 SANS 2019年的报告(上图)显示,超过70%的大型企业仍然依赖安全信息和事件管理(SIEM)系统来进行数据关联、安全分析和运营。此外,很多企业的安全运营中心(SOC)团队还围绕SIEM配备了用于威胁检测/响应、调查/查询、威胁情报分析以及流程自动化/编排的其他工具。这就产生了一个问题:如果SIEM是安全分析和运营的“重器”,为什么企业还要补充那么多其他工具? 研究表明,尽管SIEM擅长发现已知威胁并生成安全与合规性报告,但它并不适合检测未知威胁或其他安全运营场景。而且,有23%的安全专家表示SIEM平台需要大量的人员培训和经验,而21%的人则认为SIEM需要不断调优并消耗大量运营资源才能发挥作用。总之,SIEM不会很快失宠,但显然还远远不够。 安全关联的进化怪圈 “痛苦金字塔”(Pyramid of Pain) David Bianco于2013年提出的“痛苦金字塔”(Pyramid of Pain)众所周知。位于塔尖的关注重点(TTP)就是检测活动中你需要理解的那些指标。但SIEM检测发展历程及其当前状态中存在一些令人费解的谜团。本文正是要破迷解密,带您正确认知安全关联的过去与现在,揭示关于SIEM的残酷真相。 首先,让我们的思绪穿越到1999年。那时主机入侵检测系统(HIDS)是高大上的代名词,“SIEM”这词儿在Gartner分析师眼里也就是个华丽丽的新鲜玩意儿。但是,有些供应商居然已经开始开发和售卖“SIM”和“SEM”设备了。要知道,这可是1999年!设备可火了! 这就是第一批很快便会被称为SIEM的工具,拥有非常基本的“关联”规则(真的,不过是聚合和计数单条属性,比如用户名或源IP之类的),例如“多个目的地址同一端口的多个连接”、“包含SYNflood的思科PIX日志消息重复50次”、“SSH登录失败”等等。很多此类规则都非常脆弱,攻击行为的些微改变就可以规避规则触发。而且,这些规则还是设备依赖的(例如,你得针对每个防火墙设备编写此类规则)。所以,SIM/SEM供应商不得不加载成百上千条此类规则。而客户则在启用/禁用和调整大量规则的深渊中痛苦不堪。 1999年时,Dragon Squire之类主机入侵检测系统真的是90年代安全技术的一大突破,可以梳理日志,查找“FTP:NESSUS-PROBE”和“FTP:USER-NULL-REFUSED”之类的东西。 略过千禧不谈,我们快进到2003-2004时期——革命爆发了!SIEM产品横空出世,放出两个大招:规范化事件和事件分类。分析师花点时间将设备事件ID划入SIEM分类事件类型中(如:Windows事件ID 1102该往哪儿去?),然后对此编写检测规则。SIEM检测内容编写就此变得有趣! SIEM的这个巨大进步为我们带来了著名的关联规则,例如“同一目的地址远程访问类事件后的多个漏洞利用类事件”。这种规则开始支持跨设备通用检测逻辑,分析师的生活从此变得阳光明媚!通用规则的适用性强得多(就好像“任意漏洞利用”和“任意远程访问”与特定的攻击(例如VNC访问)),还能跨设备使用——只需编写一次,随后就算换了另一种防火墙,此关联规则依然能检测恶意事件。 哇哦,简直神奇!有了这个,你就(大概)能凭借几十条好规则走遍天下,无需再在几十个系统和多个操作系统版本类型的无数正则表达式、子串和设备事件ID间苦不堪言。在当时,SIEM可谓是安全产品的重大进步,这就好比汽车淘汰了马车。 此外,一些人对通用事件表达(CEE)计划寄予厚望,开始努力生成现实可用的全球日志分类和模式(大约在2005年)。 一觉醒来还在解放前 但你绝对想不到此后发生了什么! 现在,我们快进到今天,已经跨入到2020年。实际上,当前的大多数检测内容还是用的90年代风格——匹配原始日志的狭窄而精确的风格。看看Sigma内容就知道,1998年的Network Intelligence enVision SIM用户都能看懂其中大部分检测!不可否认,我们今天也有ATT&CK框架,但这解决的是另一种问题。 还有一个特别奇怪的视角:随着机器学习和分析的兴起,如果我们想要掌握更多安全用例,而不仅仅是检测,对干净的结构化数据的需求肯定会不断上升。然而,我们恰恰是只增加了数据总量,能馈送给机器学习算法的数据并没有多少。我们需要更多干净的、丰富过的数据,不是更多数据!现在不是人多力量大的时代,数据的质量更甚于数量! 这个进化过程就好像我们从马车时代走到汽车时代,然后是电动汽车,然后是喷气式汽车,结果又回到马车时代…… 那么,这些年到底发生了什么? 从挥舞关联“魔法棒”15年的ArcSight老手,到用现代工具运营检测团队的安全主管,对安全同行的调查给出了答案。 但在揭晓最终答案之前,我们不妨回顾一下调查数据收集过程中同行们都是怎么想的: 原始搜索赢了 缺乏事件规范化或规范化做得很差(或懒惰到依赖客户去做规范化工作)的产品,因不相干的原因赢得了市场(比如所收集数据的总量等),而新一代安全运营中心(SOC)分析师从没见过别的工具。于是,分析师用手头的工具勉强应付。好吧,我们暂且将此推理逻辑称之为“原始搜索赢了”。 猎手赢了 威胁猎手春风得意,把传统检测人员逼到墙角,一脚踢出窗外。现在,威胁猎手试图用自己狩猎未知威胁的方式来检测搜索已知攻击的蛛丝马迹。这可称之为“猎手赢了”。 误报赢了 另一种想法是:对“误报”(FP)的容忍度下降了(由于不断恶化的人才短缺状况),所以编写更窄的检测规则降低误报率开始流行起来(“漏报”是万万不可的——我们只能编写更多规则来阻断漏报)。规则狭窄了也更容易测试些。不妨将这种思路称之为“误报赢了”。 数据多样性赢了 还有一种假说与现代威胁和所收集数据更多样有关。由于我们需要检测更多种类的更多东西,规范化事件和分类事件就被挤到了后面。这种思路可称之为“数据/威胁多样性赢了”。 以上结论您认同哪个?您在检测工作中遇到过类似情况吗? 显然,上述解释都是盲人摸象,直觉告诉我们,SIEM的魔法并不存在,当所有碎片拼接在一起,我们逐渐看清了SIEM的残酷真相: SIEM中的规范化和分类化方法从未切实起效!在其诞生之初的2003年就没用,此后的每一年里都毫无效果。现在的环境中依然如此。除非某些事情来个大逆转,否则SIEM中规范化和分类化方法无效的事实不会有任何改变。 认识到这一点真是令人伤心,尤其是对构建、宣传、改进和试图全球标准化该方法(通过CEE)的人而言。 事情的真相真的有这么糟吗?很不幸,还真是!SIEM事件分类其实…… 总落后于时代,现在比之前落后得更多; 跨多个事件和日志源时不一致——当今每家供应商均如是; 各供应商之间差异很大——无法达成只学习一次的效果; 随时间流逝积累的错误和遗漏越来越多; 无法有效测试当今面对的真实威胁。 所以,我们甚至不能说“SIEM事件分类已死”,因为它就没真正活过。举个例子,某SIEM供应商的“身份验证失败”事件类别可能漏掉新版软件(比如Windows更新引入的新型事件),漏掉不常见日志源的事件(SAP登录失败),或者漏掉错误映射到别的东西上的事件(例如“其他身份验证”类别)。 人们总会自欺欺人,编写愚蠢的字符串匹配和基于正则表达式的内容,而一旦涉及性命攸关的入侵检测,没有人会把希望寄托在事件分类上。 下一代SIEM,取决于下一代SOC 根据安全牛SOC报告(上图),下一代SOC最主要的特征之一就是摆脱被SIEM支配的困境。 下一代SOC的“大脑”,将是SIEM、风险管理和威胁情报的融合领域,威胁情报正在成为企业打造全新高效安全运营平台,提升安全运营能力的核心。 作为企业内部安全日志的汇聚器,SIEM的基本功能永远不会过时,而且本地安全日志也将是“内生安全”中的高价值威胁情报来源(甚至比蜜罐和商业威胁情报的价值更高)。 下一代SIEM产品log Rhythm的仪表盘界面 而下一代SIEM产品,无论在云端SaaS还是本地,也将顺应SOC智能化、自动化、平台化的大趋势。(支持)开源、上云、自动化和智能化是SIEM产品的四个变革方向,我们在IBM Cloud Pak、Splunk Cloud、Exabeam、翰思和LogRhythm的产品路线中都看到了下一代SIEM的影子。 无论下一代SOC还是SIEM,甚至SOAR和TIP,其最终目标都是为新威胁形式下的风险管理和CISO的新业绩指标服务,而这些关键指标背后的安全运营变革,安全牛将在《下一代SOC报告》中详尽阐述。 【本文是51CTO专栏作者“安全牛”的原创文章,转载请通过安全牛(微信公众号id:gooann-sectv)获取授权】 戳这里,看该作者更多好文

优秀的个人博客,低调大师

【送书】最后1天,组合专栏送图书!!!

又是一年双11,剁手也得先囤课!话不多收,这是我们的双十一福利 ↓↓ 活动时间 2019年11月11日9:00——2019年11月15日24:00,嗨购111小时! 一. 买组合专栏,免费送图书 活动详情: 活动期间,凡订阅组合专栏者(不含补差价用户),可免费领取技术图书一本,限量50本,先到先得。 领取方式? ①成功订阅任一组合专栏②选定图书,书单在此>>>③扫码回复小助手【组合】 组合专栏: 《IT人必备的主流技术》《从虚拟化到容器云的实战之路》《由点及面学Linux》请添加链接描述《一线DBA的进阶指南》《从零到 一 搞定Python》《网络工程师的看家本领》《新时代网工高薪必备技能》《大牛带你解决运维典型难题》《网络工程师高手养成记》《自动化运维必备实战秘籍》 二. 全场拼团,111小时 活动详情: 活动期间,51CTO订阅专栏,全场拼团价。(原价51元,拼团只要39元) 更多福利? ①生成分享海报>>>https://blog.51cto.com/distribution/summary②邀请朋友拼团,可得现金 三. 进读者群,天降福利 活动详情: 活动期间,51CTO订阅专栏读者群内,将每天不定时发放随机福利。(参与用户至少曾经订阅过一个专栏) 福利有啥? ①纸质图书(非任选)②15元专栏专属优惠券③博文上首页增粉(仅要求原创,至少2000字,不含广告) 进群流程? 扫码回复小助手【进群】

优秀的个人博客,低调大师

【送书】最后3天,组合专栏送图书!!!

又是一年双11,剁手也得先囤课!话不多收,这是我们的双十一福利 ↓↓ 活动时间 2019年11月11日9:00——2019年11月15日24:00,嗨购111小时! 一. 买组合专栏,免费送图书 活动详情: 活动期间,凡订阅组合专栏者(不含补差价用户),可免费领取技术图书一本,限量50本,先到先得。 领取方式? ①成功订阅任一组合专栏②选定图书,书单在此>>>③扫码回复小助手【组合】 组合专栏: 《IT人必备的主流技术》《从虚拟化到容器云的实战之路》《由点及面学Linux》请添加链接描述《一线DBA的进阶指南》《从零到 一 搞定Python》《网络工程师的看家本领》《新时代网工高薪必备技能》《大牛带你解决运维典型难题》《网络工程师高手养成记》《自动化运维必备实战秘籍》 二. 全场拼团,111小时 活动详情: 活动期间,51CTO订阅专栏,全场拼团价。(原价51元,拼团只要39元) 更多福利? ①生成分享海报>>>https://blog.51cto.com/distribution/summary②邀请朋友拼团,可得现金 三. 进读者群,天降福利 活动详情: 活动期间,51CTO订阅专栏读者群内,将每天不定时发放随机福利。(参与用户至少曾经订阅过一个专栏) 福利有啥? ①纸质图书(非任选)②15元专栏专属优惠券③博文上首页增粉(仅要求原创,至少2000字,不含广告) 进群流程? 扫码回复小助手【进群】

优秀的个人博客,低调大师

【送书】最后2天,组合专栏送图书!!!

又是一年双11,剁手也得先囤课!话不多收,这是我们的双十一福利 ↓↓ 活动时间 2019年11月11日9:00——2019年11月15日24:00,嗨购111小时! 一. 买组合专栏,免费送图书 活动详情: 活动期间,凡订阅组合专栏者(不含补差价用户),可免费领取技术图书一本,限量50本,先到先得。 领取方式? ①成功订阅任一组合专栏②选定图书,书单在此>>>③扫码回复小助手【组合】 组合专栏: 《IT人必备的主流技术》《从虚拟化到容器云的实战之路》《由点及面学Linux》请添加链接描述《一线DBA的进阶指南》《从零到 一 搞定Python》《网络工程师的看家本领》《新时代网工高薪必备技能》《大牛带你解决运维典型难题》《网络工程师高手养成记》《自动化运维必备实战秘籍》 二. 全场拼团,111小时 活动详情: 活动期间,51CTO订阅专栏,全场拼团价。(原价51元,拼团只要39元) 更多福利? ①生成分享海报>>>https://blog.51cto.com/distribution/summary②邀请朋友拼团,可得现金 三. 进读者群,天降福利 活动详情: 活动期间,51CTO订阅专栏读者群内,将每天不定时发放随机福利。(参与用户至少曾经订阅过一个专栏) 福利有啥? ①纸质图书(非任选)②15元专栏专属优惠券③博文上首页增粉(仅要求原创,至少2000字,不含广告) 进群流程? 扫码回复小助手【进群】

优秀的个人博客,低调大师

JavaScript 获取 2 天之后的日期代码实例

/** *对Date的扩展,将 Date 转化为指定格式的String *月(M)、日(d)、小时(h)、分(m)、秒(s)、季度(q) 可以用 1-2 个占位符, *年(y)可以用 1-4 个占位符,毫秒(S)只能用 1 个占位符(是 1-3 位的数字) *例子: *(new Date()).Format("yyyy-MM-dd hh:mm:ss.S") ==> 2006-07-02 08:09:04.423 *(new Date()).Format("yyyy-M-d h:m:s.S") ==> 2006-7-2 8:9:4.18 */ Date.prototype.format = function (fmt) { var o = { "M+": this.getMonth() + 1, //月份 "d+": this.getDate(), //日 "h+": this.getHours(), //小时 "m+": this.getMinutes(), //分 "s+": this.getSeconds(), //秒 "q+": Math.floor((this.getMonth() + 3) / 3), //季度 "S": this.getMilliseconds() //毫秒 }; if (/(y+)/.test(fmt)) fmt = fmt.replace(RegExp.$1, (this.getFullYear() + "").substr(4 - RegExp.$1.length)); for (var k in o) if (new RegExp("(" + k + ")").test(fmt)) fmt = fmt.replace(RegExp.$1, (RegExp.$1.length == 1) ? (o[k]) : (("00" + o[k]).substr(("" + o[k]).length))); return fmt; } var now = new Date(); now.setDate(now.getDate() + 2); var d2 = now.format("yyyy-MM-dd"); d2;

优秀的个人博客,低调大师

前端优化系列 - JS混淆引入性能天坑

前言 现在用户手机性能,浏览器性能,网络性能,越来越好,后端逻辑逐渐向前端转移,前端渲染变得越来越普遍。前端渲染主要依赖JS去完成核心逻辑,JS正变得越来越重要。而JS文件是以源码的形式传输,可以在Chrome Devtools上轻易地被修改和调试。我们一般不希望核心业务逻辑轻易的被别人了解,往往会通过代码混淆的方式去进行保护。 那么,代码混淆对JS性能是否有影响呢?我们下面讨论一个真实的案例,看看混淆如何让JS性能变差100倍,并详细介绍如何去跟进和处理类似问题。 混淆引入性能问题 通常JS混淆有两种方式,一种是正则替换,强度比较弱,很容易被破解;另外一种是修改抽象语法树,比较难破解。 一些比较重要的JS文件,一般会使用修改抽象语法树的方式去进行混淆保护。相关的原理请参考知乎上的文章:前端如何给 JavaScript 加密 一般来说,JS混淆会引入多余代码,修改原来的抽象语法树,可能会引入性能问题,但性能影响一般非常小。 但是,也有异常的情况,我们在一个业务上发现它的isdsp_securitydata_send.js执行非常耗时,竟然达到惊人的1.6秒。Trace信息如下, 而使用它未混淆的源码去执行时,发现在15毫秒就执行完了。这是一个非常明显的混淆引入性能问题的案例。 分析性能问题 大部分问题,在找到根本原因之后,我们都会觉得非常简单,也很容易解决。而分析问题原因的过程和方法则更加重要,我们下面分享一些通用的分析问题的方法。 (1)确认性能问题 一般来说,确认一个JS执行是否存在性能问题,使用Chrome Trace还是比较方便的。我们下面先说说怎么看Trace信息。 上图中, v8.run对应内核的V8ScriptRunner::runCompiledScript, 代表blink端的JS的执行时间,即JS执行的实际耗时。 V8.Execute代表v8内部的JS执行时间,与v8.run代表的意义一样,耗时也相近。 颜色与V8.ParseLazy一样的部分,代表JS编译耗时,从上图可以看到,编译耗时占了绝大部分。 注:上图仅仅为了展示Trace中V8相关的含义,不是我们要讨论的JS耗时问题。 我们再来看看存在性能问题的Trace信息, 从上图可以看到,v8.run下面几乎没有蓝色的片段,即几乎没有编译耗时,基本上都是JS代码执行的耗时。 这样我们可以判断,isdsp_securitydata_send.js 执行的耗时达到了惊人的1.6秒,而这个JS的逻辑非常简单,它很有可能是存在严重性能问题的。 注:上图是isdsp_securitydata_send.js在真实环境执行消耗的时间。 (2)分析问题原因 在上面我们已经定位到isdsp_securitydata_send.js的执行耗时存在较大问题,那么可以怎么去定位问题的准确原因呢? 我们先将问题简化,把这个JS抽取出来单独去执行,比如,使用下面示例代码: <html> <body> <script type= "text/javascript" src= "https://gw.alipayobjects.com/os/secjs/a0482061-5cc3-4684-a115-ed8c61fca4b3/isdsp_securitydata_send.js" ></script> </body> </html> 然后抓取该示例代码的Trace信息, 从上面Trace可以看到,里面一些JS函数的执行非常耗时,每个耗时都有几百毫秒。 但这个外联的JS是无法定位到代码行的,我们可以将外联JS文件的内容直接拷贝到上述<script>标签里面去执行,看看具体的代码行在哪里? 从上图可以发现,耗时的代码在2117行,直接点击可以定位到具体的代码行, 从上图可以看到,下面函数执行非常耗时,耗时800多毫秒。 function a(r) { var n = Mo; var a = sn; for ( var o = S; o < r[L[No + J[Lo](U)](U) + P[Qo + Z[Lo](U)](U)]; o++) { var t = ((r[yr[No + J[Lo](U)](U) + mv + xv](o) - _) * cr + X - a) % V + _; n += String[Oa[Wo + D[Lo](U)](U) + ad + fr[Qo + Z[Lo](U)](U) + kt](t); a = t } return n } 上述函数为什么会非常耗时呢?这里就是JS引擎专家发挥的地方了!我们通过分析JS引擎的执行发现,String[Oa[Wo + D[Lo](U)](U) + ad + fr[Qo + Z[Lo](U)](U) + kt](t)这一句代码,其实是s += String.fromCharCode(p)混淆之后的结果。 这种混淆会带来什么问题呢?V8和JSC引擎的字符串拼接查找性能都非常弱,比如,String["toS" + "tring"](),number to string,都是V8和JSC引擎的超级弱点。 JS字符串拼接的性能为什么会很差呢? 在JavaScript中,字符串是不可变的(immutable),只能被另外一个字符串替换。 var combined = ""; for (var i = 0; i < 1000000; i++) { combined = combined + "hello "; } 上述示例代码中,combined+"hello "不会直接修改combined变量,而会新建一个临时对象存储计算结果,然后再使用该临时对象替换combined变量。所以上述for循环中会产生海量的临时变量,JS引擎GC需要大量工作来清理这些临时变量,从而会影响性能。注:上述解析来自Why is + so bad for concatenation? 我们再进一步去验证去掉字符串混淆的代码效果,(注:出于信息安全考虑,不提供去混淆的JS示例) <html> <body> <script type= "text/javascript" src= "https://xxx.js" ></script> </body> </html> 我们看看改动之后的JS执行的Trace信息, 从上图可以看到,isdsp_securitydata_send.js在几毫秒就执行完了。 我们再在真实的业务页面上验证优化后的效果, 执行耗时直接从1.6秒,优化为15毫秒,优化幅度大于100倍! 解决性能问题 从上面的分析可以看到,JS混淆引入了大量的字符串拼接,从而导致性能大幅下降。 那么,解决问题的方案也就很显然了,那就是去掉这些字符串拼接,即降低混淆的强度,把字符串混淆部分去掉。 去掉字符串混淆部分之后,isdsp_securitydata_send.js的执行耗时变为15毫秒,完美的实现了优化。 结束语 现在前端渲染非常流行,页面大部分逻辑由JS控制。从我们长期进行页面性能优化的经验来看,页面性能优化的20-40%与浏览器内核相关,而60-80%与前端JS相关,即前端JS是性能优化的重中之重。 那么,前端JS优化有那些比较好的实践呢?内核直接参与分析前端JS,成本非常大,并非长久之计,内核更应该做的是赋能前端。 在赋能前端方面,内核可以做那些事情呢? (1)将一些通用的前端分析方法整理成文档,供前端参考。 (2)将一些人工分析总结的经验,固化到自动化的工具,比如,WDPS Lighthouse。 (3)提供一些更有效的分析工具。比如,在Trace中更清晰的展现JS引擎的运行逻辑。 (4)与前端更多交流合作,建立互信,深入合作研究疑难问题和普遍问题。 参考文档 前端如何给 JavaScript 加密 Why is + so bad for concatenation? Optimization killers

优秀的个人博客,低调大师

2018-06-05 第三十天

一、Vector 和 ArrayList 相同点: 1:都是List 的实现的子类。 2:底层都是使用数组实现。 不同点: 1:ArrayList 是一个线程非安全的,效率更高。Vector 是一个线程安全的,效率相对较低。所以在线程安全的情况下优先考虑ArrayList 。 2:ArrayList 相比 Vector 出现的更晚。 3:扩容的规则不同:Vector %100 扩容。。ArrayList 50% 扩容。 4:Vector 可以使用枚举器 Enumeration 遍历。 二、ArrayList 和 LinkedList 相同点: 1:都属于List 实现的子类。 2:都可以通过序号访问元素。 不同点: 1:ArrayList 底层使用 数组实现。LinkedList 使用双向链表实现。 2:ArrayList 遍历的效率很高。LinkedList 遍历的效率相对较低。 3:ArrayList 根据内容查找,删除元素,插入元素,效率相对较低,因为要移动大量的元素。 三、HashMap 和 HashTable --WeekHashMap:如果一个对象的引用只被包含在了 WeekHashMap 中,垃圾回收的时候可以被回收掉。 --IdentityHashMap:key 的唯一性 通过 == 比较。实现唯一性,而不是通过key 的equals 来比较内容保证唯一性。 importjava.util.Enumeration; importjava.util.Hashtable; publicclassTestHashTable { publicstaticvoidmain(String[] args) { //Hashtable是早期的一个映射关系的容器类。 //早期继承了类Dictionary 字典类。后来Map 接口出现之后,又实现了Map 接口。 //HashMap 的key 和value 都可以是null. //Hashtable的key 和value 都不能是null。 //Hashtable是一个线程安全的容器,HashMap 是一个非安全的,效率更高。 Hashtabletable=newHashtable<>();,> table.put("cn", "China"); table.put("uk", "United Kingdom"); table.put("uk", "England"); table.put("us", "America"); // table.put("jp", null); //早期的Hashtable的遍历方式 //得到所有的key 的枚举器通过key 可以得到value Enumerationkeys= table.keys(); while(keys.hasMoreElements()) { String key= keys.nextElement(); String value= table.get(key); System.out.println(key+" --> "+ value); } //得到所有的value ,不能获得对应的key Enumerationelements= table.elements(); while(elements.hasMoreElements()) { String value= elements.nextElement(); System.out.println(" --> "+ value); } System.out.println(table); } } 四、Collection 和 Collections 不同点:一个是容器类的顶层接口,一个是用于操作容器的工具类。 java.util.Arrays:一个用于操作数组的工具类。 java.util.Collections:一个用于操作容器的工具类。 里面封装了很多操作容器对象的各种的方法。 staticEnumerationenumeration(Collectionc) 返回一个指定collection 上的枚举。 五、数组-容器 1:数组的长度是不能更改的。 所有的容器的容量都是可以根据需求增长,扩容的。 2:数组进行元素的删除和插入操作的时候,效率比较低。需要移动大量的元素。 HashMap 进行元素的删除和插入操作,效率比较高。LinkedList 效率也很高,就是需要先定位元素的位置(效率低)。 3:数组元素的类型只能是一种。 容器中只要是Object类的实例即可放到容器中,作为元素。 4:数组通过内容查找元素的效率是比较低的。 HashMap 通过内容查找元素效率比较高。 5:数组的元素是连续分配的,那么必须在堆内存中找到连续的指定内存空间才能容纳数组的所有的数据。对内存要求稍微多一些。 链表、二叉树、散列表(一部分要求连续),都没有要求元素必须是连续分配的。 6:数组没有提供任何的封装,所有对元素的操作,都是通过自定义方法来实现的。对数组元素的操作比较麻烦。 所有的容器都被封装成了类,提供了各种对元素操作的方法。 六、IO流 I:input 输入:将外部的数据读取到程序的内存中。 O:output 输出:将程序内存中的数据写出到其他的地方。 File 类:不能对文件读写。 需要通过IO 流来实现对文件的读写。 概念:流就是信息的通道。通过该通道可以实现对文件的读写操作。stream 流的分类: 1:按照数据的流向来分: 1:输入流 可以将外部的数据读取内存中的流 InputStream、Reader 2:输出流 将内存中的数据写出到目的地的流 OutputStream、Writer 2:按照流处理的最小的数据单元来分: 1:字节流 流每次处理的数据的最小单元是 1 byte。 InputStream OutputStream 2:字符流 流每次处理的数据的最小单元是 1个 char Reader Writer 字节流,什么类型的数据都可以处理。音频,视频,图片,文本。 字符流,只能处理文本数据。别的类型不能处理。处理字符功能更强。 3:按照处理的数据的源头不同来分: 1:节点流 :直接和数据源相连的流。 FileInputStream FileOutputStream 2:处理流、包装流:以其他的流为数据源的流。 BufferedInputStream BufferedOutputStream InputStream:是一个抽象类,是所有的字节输入流的父类。 OutputStream:是一个抽象类,是所有的字节输出流的父类。 Reader:是一个抽象类,是所有的字符输入流的父类 Writer:是一个抽象类,使所有的字符输出流的父类。 七、FileInputStream和FileOutputStream 和IO 相关的类 都在 java.io.*; FileInputStream: 是InputStream 的子类。 文件字节输入节点流。可以用来将文件中的内容,读取到内存中来。 FileOutputStream: OutputStream 的子类。 文件字节输出节点流。 作用:对文件进行写入数据的。 importjava.io.File; importjava.io.FileInputStream; importjava.io.IOException; publicclassFileInputOutStreamTest { publicstaticvoidmain(String[] args) { testFileInputStream2(); } //将指定的文件的内容全部读取到内容中,并打印到控制台。 staticvoidtestFileInputStream(){ //创建文件对象 File file=newFile("res/my_letter.txt"); try{ //创建流对象,在程序内存和数据源之间建立了信息的通道。 FileInputStream fis=newFileInputStream(file); //流对象提供了那些功能来读取数据源的信息。 //从数据源读取一个字节的数据,并返回返回类型是int。 //read 方法每次被调用,都会导致流中记录读取位置的游标后移一个字节。 //读取下一个字节的数据。如果流中不能在读取到数据(读取到了文件or 流的末尾返回-1) intvalue= fis.read(); while(value!= -1){//没有到达数据的末尾 System.out.print((char)value); value= fis.read(); } }catch(Exception e) { e.printStackTrace(); } } //将指定的文件的内容全部读取到内容中,并打印到控制台。 staticvoidtestFileInputStream1(){ //创建文件对象 File file=newFile("res/my_letter.txt"); try{ //创建流对象,在程序内存和数据源之间建立了信息的通道。 FileInputStream fis=newFileInputStream(file); //流对象提供了那些功能来读取数据源的信息。 byte[] buf=newbyte[10]; //每次通过流从数据源最多可以读取buf.length个字节的数据。 //被读取的字节数据保存到数组中。返回的是本次读取到的有效的字节数。 //如果读取到了文件的末尾,返回-1; intcount= fis.read(buf); while(count!= -1){ //是用字节数组生成字符串对象 String str=newString(buf,0,count); System.out.print(str); count= fis.read(buf); } //读取数据放到指定数组的区间中。 // fis.read(b, off, len) }catch(Exception e) { e.printStackTrace(); } } //测试其他的方法 staticvoidtestFileInputStream2(){ //创建文件对象 File file=newFile("res/my_letter.txt"); FileInputStream fis=null; try{ //创建流对象,在程序内存和数据源之间建立了信息的通道。 fis=newFileInputStream(file); //TODO // intcount = 0; // while((count = fis.read(buf))!= -1){ // String str= new String(buf,0,count); // System.out.print(str); // } //跳过指定的字节数 fis.skip(10); //得到文件的长度,创建足够大的数据 longlen= file.length(); byte[] buf=newbyte[(int)len]; fis.read(buf); String str=newString(buf); System.out.println(str); // 得到的文件中可以读取到的有效的字节数据个数 fis.available(); }catch(Exception e) { e.printStackTrace(); }finally{ //要确保程序可以正常的关闭流对象。如果不关闭,那么垃圾回收器都不能正常回收掉。 if(fis!=null){ try{ fis.close(); }catch(IOException e) { e.printStackTrace(); } } } } //对指定的文件写入指定的内容 staticvoidtestFileOutputStream(){ // FileOutputStream fos=null; try{ //创建流对象,当下面的语句执行的时候,会先将目的地的数据清空,然后再写入。 fos=newFileOutputStream("./res/2.txt",true);//true ,尾部添加,默认为false。 String str= "我女儿喜欢看海底小纵队,她是里面的皮医生!我呢,是呱唧!\n"; //将字符串转换为字节数组 byte[] buf= str.getBytes(); //通过流对象去写出指定内容 //将buf中的全部的字节数据写出到目的地 fos.write(buf); //fos.write(int) 将参数int的对应的字节数据写出去,每次写出一个字节。把参数的后八位写出去。 }catch(Exception e) { e.printStackTrace(); }finally{ if(fos!=null){ try{ fos.close(); }catch(IOException e) { e.printStackTrace(); } } } } //使用文件输入输出流来实现文件的复制。。如果文件不存在,那么使用FileOutputStream 往外写,那么会创建一个文件。 staticvoidcopyFile()throwsException{ // 使用文件输入流,读取字节数据,然后将读取到的字节数据,再尾部追加到指定的文件中。 FileInputStream fis=newFileInputStream("c:/dear.jpg"); FileOutputStream fos=newFileOutputStream("c:/dear_copy.jpg",true); byte[] buf=newbyte[1000]; intcount= fis.read(buf); while(count!= -1){ //读取多少数据,写出去多少数据 fos.write(buf,0,count); count= fis.read(buf); } fis.close(); fos.close(); } } 八、read 和 write 问:为什么read 读取的是一个字节,返回的却是 int? 答:为了避免读取到的字节数据是八个二进制的1 的数据(-1)。如果读取到了一个 字节数据 是 -1 ,那么和 流的末尾的条件冲突。 即使读取到了8个 二进制的1,也需要转换为int 类型。 使用int 的低八位保存读取到的一个字节数据。 11111111111111111111111111111111 000000000000000000000000 1111 1111=== 255 使用按位与&0xff来实现需求---使用int 的低八位保存读取到的 一个字节数据。 wirte(int value) 只将 value 的低八位写出去。使用(byte)value强转直接将value 的高24位全部砍掉了。 九、字符集 1:美国标准信息交换码:ASCII。 单字节的字符集,一共128个字符,只用到了一个字节数据的低7位。最高位是0. 每一个字符都对应着一个唯一的整数。 A<--->65 0<---->48 a<--->97 2: gb2312:是中国早期的一个字符集。兼容了ASCII。用一个字节的数据表示 ASCII 部分的字符。 中文简体,需要两个字节的数据才能表示一个中文字符。 3:gbk:兼容了gb2312. 增加了各个少数民族的符号,繁体中文。 中文简体,需要两个字节的数据才能表示一个中文字符。 4:utf-8:兼容了ASCII。三个字节代表一个中文。 5:Unicode:四个字节的。 十、FileReader和FileWriter以及flush FileReader: 是 Reader 的子类。是用来读取字符的。 FileReader可以每次实现从底层的字节数据中读取一个或者多个字节数据,来保证这些字节数据是一个或者几个完整的字符。要依赖于某个字符集(平台默认的GBK)。从底层读取的是字节数据(整数)去默认的字符集中 找该整数对应的字符。 所有的字符流工作的时候都要依赖于某个字符集。 importjava.io.FileReader; importjava.io.FileWriter; importjava.io.IOException; importjava.util.Arrays; publicclassTestFileReaderWriter { publicstaticvoidmain(String[] args) { // testFileReader(); copyTextFile(); } //从指定的文件中,读取全部的字符 staticvoidtestFileReader(){ FileReader fr=null; try{ //创建字符输入流对象 fr=newFileReader("./res/1.txt"); intvalue= fr.read(); while(value!= -1){ System.out.print((char)value); value= fr.read(); } }catch(Exception e) { e.printStackTrace(); }finally{ if(fr!=null){ try{ fr.close(); }catch(IOException e) { e.printStackTrace(); } } } } //使用文件字符输入输出流实现文本的复制 staticvoidcopyTextFile(){ FileReader fr=null; FileWriter fw=null; try{ fr=newFileReader("./res/my_letter.txt"); fw=newFileWriter("./res/my_letter_todear.txt"); // intvalue = 0; // while((value = fr.read())!= -1){ // //将value 写出去 // fw.write(value); // } char[] buf=newchar[10]; intcount= 0; while((count= fr.read(buf)) != -1){ System.out.println(Arrays.toString(buf)); //这个write 方法,并没有直接将字符数据写出到目的文件中, //写到了一个缓冲区中。为了减少直接对磁盘的IO 提高效率 //解决方案:1 把流正常关闭,关闭流的时候,所有的相关的缓冲区中的数据都将会被刷新到目的地。 //2:将缓冲区的数据,刷新出去。 fw.write(buf, 0, count); } //将缓冲区的数据,刷新出去到目的地。 fw.flush(); }catch(Exception e) { e.printStackTrace(); }finally{ if(fr!=null){ try{ fr.close(); }catch(IOException e) { e.printStackTrace(); } } if(fw!=null){ try{ fw.close(); }catch(IOException e) { e.printStackTrace(); } } } } } 十一、BufferedInputStream和BufferedOutputStream 带缓冲区的字节输入输出流。为了提高读写的效率。 importjava.io.BufferedInputStream; importjava.io.BufferedOutputStream; importjava.io.FileInputStream; importjava.io.FileOutputStream; importjava.io.IOException; publicclassBufferedInputOutputStreamTest { publicstaticvoidmain(String[] args) { longtime= System.currentTimeMillis(); copyFile(); longcost= System.currentTimeMillis()-time; System.out.println("buffered cost = "+cost); time= System.currentTimeMillis(); try{ copyFile1(); }catch(Exception e) { e.printStackTrace(); } cost= System.currentTimeMillis()-time; System.out.println("cost = "+cost); } //带缓冲区实现文件复制 staticvoidcopyFile(){ BufferedInputStreambis=null; BufferedOutputStream bos=null; try{ //这是一个包装流,以其他的流为数据源读取数据。为了提高效率的 bis=newBufferedInputStream(newFileInputStream("c:/dear.jpg")); bos=newBufferedOutputStream(newFileOutputStream("c:/nan.jpg")); byte[] buf=newbyte[100]; intcount= bis.read(buf); while(count!= -1){ bos.write(buf, 0, count); count= bis.read(buf); } }catch(Exception e) { e.printStackTrace(); }finally{ if(bis!=null){ try{ bis.close(); }catch(IOException e) { e.printStackTrace(); } } if(bos!=null){ try{ bos.close(); }catch(IOException e) { e.printStackTrace(); } } } } //使用文件输入输出流来实现文件的复制。。如果文件不存在,那么使用FileOutputStream 往外写,那么会创建一个文件。 staticvoidcopyFile1()throwsException{ // 使用文件输入流,读取字节数据,然后将读取到的字节数据,再尾部追加到指定的文件中。 FileInputStream fis=newFileInputStream("c:/dear.jpg"); FileOutputStream fos=newFileOutputStream("c:/dear_copy.jpg",true); byte[] buf=newbyte[1000]; intcount= fis.read(buf); while(count!= -1){ //读取多少数据,写出去多少数据 fos.write(buf,0,count); count= fis.read(buf); } fis.close(); fos.close(); } }

优秀的个人博客,低调大师

2018-05-22 第十六天

Â一、 三种内存区域 java 的jvm 内存分为 3部分: 1:java 栈 stack : 所有的局部变量都在栈中分配空间。 静态内存分配的。每个方法调用的时候占用多少内存在编译期就确定了。 栈内存是连续分配的,因此栈中数据的存取效率略低于 cpu 寄存器,效率比较高。 所以,如果在方法内部能用局部变量实现的功能,尽量使用局部变量。 栈内存的回收是方法返回自动回收。 2:java 堆 heap: 所有的new 出来的对象都在堆中分配空间。 如数组,对象等等。 堆是jvm 动态管理的。在运行期对创建的对象进行空间动态的分配。 内存管理灵活,但是效率相对较低。 堆内存有专门的进行垃圾内存回收的机制(精彩内容待续...) 3:方法区 method area: 运行时需要的数据存储区:不回收。 1.类的字节码元数据(模版数据)。 2.常量池 3.静态数据区 从物理内存上来讲堆和方法区都属于堆。因为存放管理的数据不同,加以名字上的区分。 二、 this this:java 一个关键字。 1:编译器在编译之后,会在所有的实例方法中(包含构造方法)访问的本类的实例成员(实例成员量、实例成员方法)的前面默认的添加this。 什么是this:this是对象(object)的引用(reference)。 是当前对象的引用。 什么是当前对象? 当前被调用的方法,哪个对象调用的该方法,那么该对象就是当前对象。 this作用: 1:实现了不同的对象访问同一个实例方法,方法内部访问的是不同对象的属性。 2:使用this 区分局部和成员变量,如果局部变量和成员变量名字冲突。 3:在类的构造方法内的 第一句访问本类的其他的构造方法。 例: packagecom.bjsxt.oop0522; //自定义个描述人的类Person:属性:age,name,gender,weight,count(记录生成了几个Person 对象只能使用现有知识) //提供一个默认无参的,一个带参数的构造方法,构造方法内对成员变量赋值,使用this区分。 //定义一个Person数组,元素个数为6 ,然后使用count 属性,用来统计创建的Person对象的个数。 // //定义方法:show 展示自己的属性。定义方法eat 修改自己的属性weight 。 //将数组中所有的对象依次调用eat 和show 方法。 // //最后,通过每个对象的count 属性,都可以得到创建了几个Person对象。 publicclassTestStatic { publicstaticvoidmain(String[] args) { finalintCOUNT= 6; Person[] persons= newPerson[COUNT]; persons[0] = newPerson(12, "小花", "女", 99); persons[1] = newPerson(13, "小刚", "男", 60); persons[2] = newPerson(16, "小兰", "女", 50); persons[3] = newPerson(17, "小明", "男", 100); persons[4] = newPerson(19, "韩梅梅", "女", 110); persons[5] = newPerson(20, "翠花", "女", 130); for(Person person: persons) { person.eat(); person.show(); } } } classPerson{ intage; String name; String gender; intweight; staticintcount; publicPerson() { } publicPerson(intage, String name, String gender, intweight) { this.age= age; this.name= name; this.gender= gender; this.weight= weight; count++; } voideat(){ System.out.println("人多吃蔬菜水果,对身体好!"); weight++; } voidshow(){ System.out.println("name = "+name+ "\tage = "+age+ "\tgender = "+gender+ "\tweight = "+weight+ "\tcount = "+count); } } 三、 Static static:是java 的一个关键字,静态的意思。 static 可以用来修饰类的成员变量,还可以修饰成员方法(有限制的修饰,并不是所有的都需要,或者是都可以)。 static 修饰成员变量:内存分配的时机: 当在代码中访问某个类的时候,第一步要对类进行类加载的判断,如果没有加载,那么先加载类的字节码到方法区。 类加载:装载(将class文件字节码信息装载到内存)--->链接--->类的初始化(对类的静态成员变量进行空间的分配(第一步对静态变量进行空间的分配并执行默认初始化,再进行声明处的赋值按照从上到下的顺序)) 特点: 1:类加载只有一次,所以静态的成员变量内存只分配一次。只有一份内存空间(方法区)。 2:静态成员变量的内存不依赖于任何的对象。依赖于所在的类。(类变量--区分--实例变量) 3:生命周期依赖于加载的类。java 程序结束了回收。 4:所有的该类的对象都可以访问 static 变量的内存空间。被所有的对象共享的。可以通过对象引用访问静态成员变量。 5:除了可以通过对象访问静态成员变量,还可以通过类名直接访问静态成员变量。两种访问机制(对象.静态变量名 或 类.静态变量名) --什么样的成员变量可以使用static 修饰? 1.不可变的:staticfinalintMAX_AGE= 135; 2.需要优先赋值的。 static 修饰成员方法: 静态成员方法特点: 1:类加载完毕之后就可以使用了。不需要创建对象。 2:可以使用对象调用静态方法,也可以使用类名调用方法。(本类访问静态成员,通常类名省略) 作用: 1:可以用来访问静态成员变量(只能是局部 或静态成员)。(通过静态的方法访问静态成员变量) 2:可以用来作为工具方法。(Math 数学工具类不需要创建对象直接用类名来访问静态方法 Arrays)。 注意:main 方法 是静态的。可以保证 包含入口的类一旦加载,就可以执行main 方法,而不需要创建所在类的对象。 思考: 1:静态方法是否可以访问类的实例变量? 不可以:静态方法可以调用的时候,不一定创建了对象,实例变量的内存是依赖于对象的。 2:静态方法中,是否可以有this? 不可以:同上 3:类的实例方法中是否可以访问静态成员? 可以,对象已经生成,那么类加载早已经完成,静态成员的内存空间已经分配了,可以安全访问。 四、 代码块 类中包含的内容: 成员变量(实例成员变量、静态的)、成员方法(实例的,静态的)、构造方法。 1:局部代码块:在方法中的一对大括号 。 作用:可以将局部变量的作用域范围控制的更小。 2:构造块:在类的内部,方法的外部的一对大括号。 特点:每生成一个该类的对象,构造块就会被执行一次。先于构造方法执行。 作用:对对象属性进行初始化的。 3:静态块:在类的内部,方法的外部 static{} 特点:在类加载的时候,类初始化的那一步static 代码块被执行,且仅执行一次。 作用:可以在静态块中对静态的成员变量进行初始化。对一些只执行一次,而且希望优先执行的功能,可以放在静态块中执行。 注意:静态块中不能访问实例成员。 publicclassTestBlock { publicstaticvoidmain(String[] args) { //局部代码块 { intage= 10; System.out.println(age); } Tree tree= newTree(); newTree(); } } classTree{ intage= 10; //构造块 { System.out.println("Tree.enclosing_method()"); } //静态代码块 static{ System.out.println(" - -static - - Tree.enclosing_method()"); } publicTree() { System.out.println("Tree.Tree()"); initTree(); } voidinitTree(){ age= 100; } } 五、 生成一个对象的过程: 1:类加载,在类加载的最后,类初始化部分,对 静态成员变量进行初始化,并执行静态代码块。 2:由jvm 在堆中分配一块内存供对象使用,并对对象的所有的属性进行默认初始化。 3:按照从上到下的顺序执行成员变量声明处的赋值以及构造块中的代码。 4:执行构造方法的代码。 5:构造方法执行完毕,对象初始化完毕。由new 关键字将生成对象的内存地址返回 。 六、 封装 面向对象的三大特性:封装encapsulation、继承 inheritance、多态 ploymofisim。 程序是用来处理数据的。 java 程序的基本的组成单元就是类。 java 处理的程序的数据就是类中的属性,成员变量。 封装:希望被别的对象访问的数据和功能对外提供接口,不希望被外部访问的部分隐藏起来。这个过程称为封装。 通过什么样的方式来实现对数据和功能的隐藏和暴露? 如何控制类成员的被访问的权限? java 中提供了3个 四类 权限访问修饰符。来实现对类成员的访问权限的控制。 java 中的类成员的访问权限修饰符: public:公有的意思。被访问的最大的权限。 工程级别的: 本类访问、本包其他的类、跨包子类、跨包的其他的类。 protected:受保护的: 访问权限: 本类、本包其他的类、跨包子类。 默认的: 访问权限:包私有权限 本类、本包其他的类。 private :私有的。 访问权限: 本类访问。 类成员的访问权限的一些使用的基本规律: 1:所有的成员变量全部私有化。如果需要其他的类访问私有的成员,通过统一的对外的修改器和访问器实现 accessor。 getter and setter。 2:类的所有的成员的访问权限要尽量的小。 类的访问权限:只有两个: public:本工程的所有的类都可以访问public 修饰的类。 默认的:本包的其他的类可以访问 默认权限的 类。 例: /** * 封装 * @authoryhl * 定义一个Student类,属性,age,name,score,gender, 所有属性全部私有化。 然后对所有的成员变量,提供访问器和修改器访问器getter,public ,修改器setter 访问权限default。 定义构造方法,通过参数设置属性的值(不要直接对属性赋值,而是通过方法赋值)! 定义show 方法,展示属性。 定义无参的构造方法。 定义测试类: 生成Student对象,通过有参的构造方法,然后,调用show 方法。 再生成一个Student对象,调用无参的构造方法。通过修改器对所有的属性赋值。然后调用show 方法。 * */ publicclassTestEncapsulation { publicstaticvoidmain(String[] args) { Student student= newStudent(12,"小花",99,"女"); student.show(); Student student2= newStudent(); //链式 student2.setAge(17).setName("小刚").setGender("男").setScore(100).show(); } } classStudent{ privateintage; privateString name; privateintscore; privateString gender; publicStudent() { } publicStudent(intage, String name, intscore, String gender) { setAge(age); setName(name); setScore(score); setGender(gender); } publicintgetAge() { returnage; } publicString getName() { returnname; } publicintgetScore() { returnscore; } publicString getGender() { returngender; } Student setAge(intage) { this.age= age; returnthis; } Student setName(String name) { this.name= name; returnthis; } Student setScore(intscore) { this.score= score; returnthis; } Student setGender(String gender) { this.gender= gender; returnthis; } publicvoidshow() { System.out.println("Student [age="+ age+ ", name="+ name+ ", score="+ score+ ", gender="+ gender+ "]"); } }

优秀的个人博客,低调大师

2018-05-14 第八天

一、 分支选择: java 的流程控制语句: 控制代码执行的一个路径、方向。 三类:1.顺序执行:从上到下依次执行代码。 2.分支选择:根据条件 来 选择执行某些代码,跳过某些代码。 3.循环:某些代码反复的被执行。 switch:常用于等值比较的多分支选择。 if-else if -else :既可以使用等值比较判断,还可以进行不等值的比较的多分支选择。 switch 能实现的,if-else if -else能比较轻松地实现。 而if-else if -else 能实现的,switch 可能实现起来很繁琐。 因此,不等值的比较常用:if-else if -else 等值比较根据case 的数量决定使用哪个:switch、if-else if -else。 switch优点:结构清晰一些。 (一)if 选择语句: 1.if 单分支选择: 语法: if(条件表达式){ //任意行java 代码 } //java 后续代码 解释: if 是关键字,如果 的意思。 条件表达式:必须是一个返回值为 boolean 类型的表达式。 往往是关系表达式、逻辑表达式。 {}:if 代码块。看做一个执行的整体。 执行过程: 1:如果 条件表达式成立即返回值为 true,那么就执行if 代码块中的所有的代码。 2:如果条件表达式不成立即返回值为false,那么就跳过if 代码块中的所有的代码,执行java 后续的代码内容。 注意: 1.如果 if 代码块 只有一条语句,那么大括号可以省略。if 只控制这一条代码的执行与否。 2.条件表达式的小括号的后面,千万不要加分号,加上分号之后,条件控制的就是分号是否执行。和后面的代码块就没有一点关系了。 2. if-else:双分支选择语句。 语法:if(条件表达式){ //if 代码块 }else{ //else 代码块 } //后续代码 解释: else:java 关键字否则的意思 执行过程: 1:如果 if 的条件成立即条件表达式的返回值为true。那么执行 if 代码块的内容。执行完 if 代码块的内容后,跳过else 代码块。直接执行后续代码。 2:如果 if 条件不成立即返回false。那么跳过 if 代码块 的内容,直接执行else 的代码块,执行完毕后,执行后续代码。 注意: 1:if 和 else 代码块必须执行且只能执行一个代码块。 2:if 代码块 和 else 代码块 ,任意一个代码块 如果只有一条语句,大括号可以省略。 3:if 和 else 条件的后面不能添加“;” 4: if 语句可以单独存在,但是else 是不可以单独存在的。else 必须和某一个if 配对。 3.多分支:if else if else 语法: if(条件-1){ //代码块-1 }else if(条件-2){ //代码块-2 }else if(条件-2){ //代码块-3 }else if(条件-2){ //代码块-4 }.... //任意个else if else{ //代码块-n } //后续代码块 执行过程: 1:先判断条件-1成立即返回true,执行代码块-1 然后跳过后续的所有的 代码块-n,执行后续代码。 2:如果条件-1 不成立即返回false,跳过 代码块-1 继续 判断条件-2,如果成立,则执行代码块-2,并跳过后续的所有条件和代码块-n ,直接执行后续代码。如果条件-2不成立即返回false,则跳过代码块-2 继续判断条件-3,依此类推。 3:简单概述:if 后面的条件,如果有一个成立的,那么就执行 对应的代码块。其他的代码块 都不执行。 4:如果所有的条件都不成立,那么,执行最后的 else 代码块-n。 注意的问题: 1:所有的代码块-1 到 -n,必须执行且仅执行一个。条件互斥的代码块。 2:else 的数量必须小于等于 if 的个数。最后的else可以没有。如果最后的else没有,那么可能没有一个代码块-n 被执行,即所有条件都不成立。 3:else 和它前面的最近的if 配对。 例1: // 需求:产生一个随机数,范围[0-22]. 来代表一个年龄 age, // 使用if 语句,如果 age [0-3] 输出 还没有上学 [4-6] 幼儿园小朋友 [7-12] 小学生 [13-15] 中学生 [16-18] 高中生 [19-22] 大学生。 public class TestIfElseIf{ public static void main(String[] args){ final int MIN = 0; final int MAX = 23; //得到一个随机年龄 int age = (int)(Math.random() * (MAX-MIN) + MIN); System.out.println("age = "+age ); if(age <= 3){ System.out.println("还没有上学!"); }else if(age <= 6){ System.out.println("幼儿园小朋友!"); }else if(age <= 12){ System.out.println("小学生!"); }else if(age <= 15){ System.out.println("中学生!"); }else if(age <= 18){ System.out.println("高中生!"); }else /*if(age >= 19 && age <= 22)*/{ System.out.println("大学生!"); } } } 例2: //需求:模拟一次打斗 随机产生一个人的生命值hp[50-100],然后打印。 然后才随机产生一个伤害值[30-60],暴击的概率为60%,1.5倍伤害 。。 //然后将伤害值也打印 //如果出现暴击的话,要提示。然后根据此次的伤害和生命值,最后输出 人 是否被 over掉。 如果没有over,输出 最后的血量 hp。 //暴击是否出现的算法:产生一个随机数 0-1, 可以把0 or 1 任意一个值出现的时候认为出现暴击就可以了。 public class TestIfElse{ public static void main(String[] args){ final int MAX_HP = 101; final int MIN_HP =50; int hp = (int)(Math.random() * (MAX_HP-MIN_HP)+MIN_HP); System.out.println("hp = "+hp); final int MIN_DAMAGE = 30; final int MAX_DAMAGE = 61; //60%的暴击 final double CRITICAL_PERCENT = 0.6; final double CRITICAL_DAMAGE = 1.5; int damage = 0; damage = (int)(Math.random() * (MAX_DAMAGE-MIN_DAMAGE)+MIN_DAMAGE); //定义一个变量,来表示本次攻击是否产生了暴击 boolean isCritical = false; //产生一个随机数,[1-100] ran ran [1-60] //Math.random() [0.0~1.0) [0.0~0.6] double ran = Math.random(); if(ran <= CRITICAL_PERCENT){//产生暴击 isCritical = true; } //根据是否产生了暴击,进行暴击的加成 if(isCritical){ damage *= CRITICAL_DAMAGE; //damage = (int)(damage * CRITICAL_DAMAGE); } //减血 hp -= damage; //控制hp 不能小于0 hp = hp < 0 ? 0 : hp; if(isCritical){ System.out.println("本次伤害为【暴击】伤害!伤害值为:"+damage +"\t剩余血量为:"+hp); }else{ System.out.println("本次伤害为普通伤害!伤害值为:"+damage +"\t剩余血量为:"+hp); } //输出人是否over了 if(hp > 0){ System.out.println("人还活着!剩余血量:"+hp); }else{ System.out.println("逝者长已矣!大家好好活着。"); } } } 4.switch:多分支选择语句。 switch:开关。 语法:比较多见的形式: switch(表达式-1){ case 常量表达式: //任意行的java 代码 break; case 常量表达式: //任意行的java 代码 break; case 常量表达式: //任意行的java 代码 break; ... default: //任意行的java 代码 break; } 解释: 1:表达式-1: 必须是一个变量,或者是变量表达式。 ----表达式的返回值类型只能是:char 、byte、short、int、enum(jdk1.5)、String(jdk1.7)。 2:case 后的表达式:必须是常量 或者是 常量表达式 3:default:是关键字 ,默认的意思。 执行过程: 1:首先计算表达式-1 的返回值。 2:用 表达式-1 的返回值 依次 和 每一个case 后的表达式的返回值进行比较,如果两个值相等。那么就执行 case 和 下一个break 之间的代码。 执行到break 结束整个switch 语句。 3:如果表达式-1 的返回值和每一个case 后的值都不相同,那么就执行default 后的java代码。遇到break 结束 switch 语句。 注意: 1:表达式-1 和 所有的case 后的表达式的返回值的类型要兼容。 2:每一个case 后的表达式的返回值都必须和其他的case 是不同的。 3:default 和其后的 break; 语句是可以没有。 4:每一个case 后的 break,都是可以没有的。 但如果case 没有对应的break ,那么执行的效果是?后面的case 语句,被当作了普通的java 代码被顺序执行。直到遇到了一个break 语句,才能结束整个switch 语句。 5:break 是打断的意思。在switch 语句中的作用:就是为了结束switch 语句。 例: // 随机得到一个整数 [0-100] ,定义分数变量 score 用来存储该随机数。打印当前分数 // 然后,如果score在[90-100] 打印 成绩为A级。 在[80-89] 打印为B级 [60-79] C 级 剩余部分 为 D级。 // 使用switch 实现。 public class TestSwitch{ public static void main(String[] args){ final int MIN = 0; final int MAX = 101; int score = (int)(Math.random() * (MAX-MIN) + MIN); System.out.println("score = "+score ); score/=10; switch(score){ case 10: case 9: System.out.println('A'); break; case 8: System.out.println('B'); break; case 6: case 7: System.out.println('C'); break; default: System.out.println('D'); break; } } } 二、 循环--loop 循环的分类:四种:1.while 循环 2.do-while 循环 3.for 循环 4.for-each 循环,专门针对容器的循环。 jdk1.5 循环的构成:1:循环体 2:循环的控制部分:a:循环控制初始化;b:循环条件 3:循环迭代部分 (一)while 循环: 语法:while(循环条件){ //循环体---需要反复执行的java 代码。 } //后续代码 解释: 1:循环条件: 必须是一个boolean 表达式。返回一个布尔值。根据该布尔值的结果来判断是否需要执行某些代码。 2:{}:中的内容称之为循环体即需要反复执行的某些代码。 执行过程: 1:先判断循环条件是否成立,如果是true 那么就执行循环体中的代码。循环体执行完毕之后,继续判断循环条件,成立,继续执行循环体。 2:反复判断,执行的过程。直到判断到循环条件不成立的时候--false。那么就直接结束整个循环。执行后续的代码。 注意: 1: 如果循环体的内容只有一条java 语句,那么循环体的大括号可以没有。如果循环体是多条语句,那么必须使用大括号括起来。 2:千万不要在循环条件后添加分号。 3:通常会在循环体中对循环条件进行改变,以达到在适当的时候结束循环。 4:while 循环有可能循环体一次都不执行。即一开始循环条件不成立,那么循环体就一次都不执行。 例: public class TestWhile1{ public static void main(String[] args){ // 需求:有100块钱 买矿泉水 一瓶水2块钱,空瓶可以退1元 瓶盖还可以退double 0.5元 问可以买多少瓶水? double money = 100; final int WATER_PRICE = 2; final int EMPTY_BOTTLE_PRICE = 1; final double BOTTLE_COVER = 0.5; int counter = 0; while(money >= WATER_PRICE){ //买水 money -= WATER_PRICE; counter ++; //喝水,退瓶,退瓶盖 //System.out.println("咕咚~~~~"); money += EMPTY_BOTTLE_PRICE; money += BOTTLE_COVER; } System.out.println("counter = "+counter); System.out.println("money = "+money); // 需求 产生一个随机数[10-100] [该随机数必须可以被7整除,还可以被5整除] 使用 do-while 实现 final int MIN = 10; final int MAX = 101; int num = 0; do{ num = (int)(Math.random()*(MAX-MIN)+MIN); }while(/*!(num%7==0 && num % 5 == 0)*/num%7!=0 || num%5!= 0); System.out.println("num = "+num); //只要num 余7 或者 余5 不等于0 } } (二)do-while 语法:do{ //循环体 }while(循环条件); 执行过程: 上来就先执行一次循环体,然后再进行循环条件的判断。成立-true,继续执行循环体,不成立,false 直接结束循环。 注意: 1:循环体是一句java 代码,大括号可以省略。 2:循环条件后的分号 ; 必须存在。 3:do-while 至少会执行一次循环。while 有可能一次都不执行循环体。 使用的环境: 如果循环条件判断要依赖于循环体得到的结果,那么可以考虑使用 do-while。 但是仍然可以使用while 替代 do-while 在循环条件之前先得到一个结果。然后再判断。 例:石头剪刀布小游戏 // 和计算机 石头剪刀布,1,石头,2,剪刀,3,布。5局3胜。循环实现。 // 分析:定义的变量:终态变量,石头,剪刀,布, 最大的胜利的场数 import java.util.Scanner; public class TestGame{ public static void main(String[] args){ //终态变量,石头,剪刀,布 final int ROCK = 1; final int SCISSORS = 2; final int PAPER = 3; // 最大的胜利的场数 final int MAX_WIN_COUNT = 3; //电脑随机的数值 int computerInput = 0; //玩家输入 int playerInput = 0; //电脑胜利的次数 int computerWinCount = 0; //玩家胜利的次数 int playerWinCount = 0; //计数器 当前的局数 int counter = 1; //创建一个扫描器对象接收键盘输入 Scanner scanner = new Scanner(System.in); // while(computerWinCount < MAX_WIN_COUNT && playerWinCount < MAX_WIN_COUNT){ //局数显式 System.out.println("********************************第 "+counter+" 局游戏开始了*************************"); //先让玩家输入,提示输入的信息 System.out.println("请输入您的选择数字:[1-->石头][2-->剪刀][3-->布]"); playerInput = scanner.nextInt(); //电脑随机一个选择 computerInput = (int)(Math.random()*(PAPER + 1 - ROCK) + ROCK); // 打印电脑和玩家选择的内容 if(playerInput == ROCK){ System.out.println("玩家选择的是【石头】"); }else if(playerInput == SCISSORS){ System.out.println("玩家选择的是【剪刀】"); }else if(playerInput == PAPER){ System.out.println("玩家选择的是【布】"); } switch(computerInput){ case ROCK: System.out.println("电脑选择的是【石头】"); break; case SCISSORS: System.out.println("电脑选择的是【剪刀】"); break; case PAPER: System.out.println("电脑选择的是【布】"); break; } //比较电脑和玩家本局的结果,打印提示信息,修改胜利的局数。 //把所有的可能的情况都罗列出来,给出对应的解决方案, if(playerInput == ROCK){//玩家选择的是石头 switch(computerInput){ case ROCK: System.out.println("本局是平局!!!"); break; case SCISSORS: System.out.println("本局【玩家】胜利!!!"); playerWinCount++; break; case PAPER: System.out.println("本局【电脑】胜利!!!"); computerWinCount++; break; } }else if(playerInput == SCISSORS){//玩家选择的是剪刀 switch(computerInput){ case ROCK: System.out.println("本局【电脑】胜利!!!"); computerWinCount++; break; case SCISSORS: System.out.println("本局是平局!!!"); break; case PAPER: System.out.println("本局【玩家】胜利!!!"); playerWinCount++; break; } }else if(playerInput == PAPER){//玩家选择的是布 switch(computerInput){ case ROCK: System.out.println("本局【玩家】胜利!!!"); playerWinCount++; break; case SCISSORS: System.out.println("本局【电脑】胜利!!!"); computerWinCount++; break; case PAPER: System.out.println("本局是平局!!!"); break; } } //打印当前比分 System.out.println("当前比分【电脑】vs【玩家】 == "+ computerWinCount +" vs "+playerWinCount); System.out.println("********************************第 "+counter++ +" 局游戏结束了*************************"); } //打印游戏的最终结果 if(playerWinCount == MAX_WIN_COUNT){ System.out.println("【玩家】胜利了,最终比分:【电脑】vs【玩家】 == "+ computerWinCount +" vs "+playerWinCount); }else{ System.out.println("【电脑】胜利了,最终比分:【电脑】vs【玩家】 == "+ computerWinCount +" vs "+playerWinCount); } } }

优秀的个人博客,低调大师

2018-05-11 第五天

一、条件运算符: java 中唯一的一个三目运算符。 需要三个操作数(三个表达式)。 语法:表达式-1 ? 表达式-2 :表达式-3 ; 表达式-1: 必须是一个boolean 表达式(返回值是boolean值的表达式)。 表达式-2,3 是java 任意的表达式,但是两个表达式的返回值的类型要兼容。 执行过程: 1:计算表达式-1的返回值。 2:根据表达式-1 的返回值 ,如果是true,那么就执行表达式-2 并返回该值作为整个条件表达式的返回值。 3:如果 表达式-1 的返回值 是 false,那么就跳过表达式-2 ,执行并返回表达式-3 的值。 //条件运算符 public class TestThree{ public static void main(String[] args){ // 随机得到两个随机数,[10-30],打印结果。 // 将两个随机数中比较大的值输出。 //定义区间 final int MIN = 10,MAX = 31; int num1 = (int)(Math.random()*(MAX-MIN)+MIN); int num2 = (int)(Math.random()*(MAX-MIN)+MIN); int num3 = (int)(Math.random()*(MAX-MIN)+MIN); System.out.println("num1 = "+ num1 + "\tnum2 = "+num2 + "\tnum3 = "+num3); int max = 0; // max = num1 > num2 ? num1 : num2; // max = max > num3 ? max : num3; max = num1 > num2 ? (num1 > num3 ? num1 : num3) : (num2 > num3 ? num2 : num3); System.out.println("max = "+max); } } 二、 运算符优先级 规律: 1:乘除取余 优先级大于 加减 2:同级优先级 根据结合性结合。 3:小括号优先级最高。 4:一元运算符大于二元的,二元的大于三元,赋值运算符优先级最低。 口诀:单目算术为关系,逻辑条件后赋值。 单目:单目运算符! + –(负数) ++ --等 乘除:算数单目运算符* / % + - 为:位移二目运算符<< >> 关系:关系二目运算符> < >= <= == != 逻辑:逻辑二目运算符&& || & | ^ 条件:唯一的三目运算符A > B ? X : Y 后:无意义,仅仅为了凑字数 赋值:赋值= 三、 算术运算时数据类型的转换 注意:java 不支持不同类型的数据之间的运算。 如果是不同类型的数据进行运算,那么编译器会对不同的类型的数据进行类型的统一的转换。转换为统一类型之后才能做运算。 自动类型转换的规则:因为这种转换是安全的。 1:如果操作数 的类型 是 byte,char ,short ,int 中的一个或者几个。那么这些操作数的类型要被自动转换为 int 类型然后再做运算。结果也是int。这种转换不会修改数据的大小,只是数据使用内存空间的大小的变换。 2:如果操作数 的类型 是 byte,char ,short ,int,long 中的一个或者几个,而且有long类型的数据存在。那么这些操作数的类型要被自动转换为long类型然后再做运算。结果也是long。 3:如果所有的操作数的类型中,最大的类型是float,那么所有的操作数的类型都向float转,结果也是float。 float 类型表示的范围大于 long。 4:如果所有的操作数的类型中,最大的类型是double,那么所有的操作数的类型都向double转,结果也是double。 double 类型表示的范围最大。 自动类型转换: 从 int 到 float,有可能损失精度。 int 到 float 可能损失精度,int 最大 2147483647, float 有效数字个数是 6-7, 会失去有效数字个数引起的精度。float 到 int 会失去小数部分,也就损失精度了。另外, float允许数值范围大于int,超出int允许范围。 四、 赋值时转换 赋值的时候的类型的转换。 1:自动向上类型转换:可以将一个小类型的数据直接赋值给一个大类型的变量。因为安全。小类型向大类型自动转换,因为安全。 2:强制向下类型转换: 整数的强制向下;直接将高位砍掉。 浮点数 到 整数 强转;先直接砍掉小数点。然后再按照整数的规则砍。 强制转换不能确保是安全的。 public class TestChangeType{ public static void main(String[] args){ int age = 10; byte value = 10; int num = value; System.out.println(num); byte value1 = 12; byte value2 = 10 + 23;//没有问题 // 在编译期,是不知道变量的值的。但是对于常量值,编译器是可以判断赋值 是否安全。 byte value3 = (byte)(value1 + value2); System.out.println((short)128.349329329);//-128 1000 0000 0111 1111 } } 字符串连接时的数据转换: "123" + 123:字符串和其他的类型也是不能直接连接的,其他的类型需要转换为字符串才能和字符串做连接。这个基本数据类型的数据到字符串的转换,是由底层的方法调用来完成。 例1: import java.util.Random; /*得到一个随机字符[A-Z]or[a-z] (大小写也要随机),并打印。*/ public class TestRandomBoolean{ public static void main(String[] args){ Random ran = new Random(); //定义区间[A-Z][65-90][a-z][97-122] final int MIN_1 = 65,MAX_1 = 90,MIN_2 = 97,MAX_2 = 122; System.out.println(ran.nextBoolean() ? (char)(Math.random()*(MAX_1 - MIN_1) + MIN_1) : (char)(Math.random()*(MAX_2 - MIN_2) + MIN_2)); } } 例2: import java.util.Random; /*随机生成两个数,[0-100],然后输出,然后实现用一个int类型的变量num 保存两个数的算法.*/ public class TestMemory{ public static void main(String[] args){ Random ran = new Random(); //定义区间[10-100] final int MIN = 10,MAX = 101; int num1 = (int)(Math.random()*(MAX - MIN) + MIN); //获得的随机数可能为负,>>> 1 转换成 正整数; int num2 = (ran.nextInt() >>> 1) % (MAX - MIN) + MIN; System.out.println("第一个数 = " + num1 + "\t第二个数 = " + num2 ); int num3 = num1 << 16; int num4 = num2 | num3; System.out.println("存储的int型 = " + num4 ); int num5 = num4 & 0b1111111111111111; int num6 = num3 >> 16; System.out.println("==========================================\n解出来第一个数 = " + num6 + "\t解出来第二个数 = " + num5 ); } }

优秀的个人博客,低调大师

2018-05-08 第二天

一、Java的特点: Java是简单的: 不再有#include 和#define 等预处理功能 不再有struct,union及typedef 不再有函数、 不再有指针、不再有多继承(但可以多重继承) 不再有goto 不再有操作符重载(Operatior Overloading) 不再有全局变量取消自动类型转换,要求强制转换 不再有手动内存管理 Java是安全的:取消了指针,自动内存回收。 Java是完全面向对象的:继承、封装、多态 Java是健壮的:Java的强制类型机制、异常处理、垃圾的自动收集等是Java程序健壮性的重要保证 java 最大的特点和优势: java 跨平台。 二、Java及C语言的跨平台对比: Java跨平台需要先编译再解释成不同平台可以执行的字节码文件,且一次编译多处执行;而C语言直接编译成与本地平台相关的二进制数据文件,对于不同平台需要多次编译,多次执行。 1.java的跨平台原理: jvm 的作用: 1:类加载 2:内存的管理。 3:解释给本地平台去执行字节码。 2.C语言的跨平台原理: 三、Hello World public class HelloWorld{//HelloWorld是类的名字。要和文件的名字一致。 //类体 public static void main(String[] args){//应用程序的入口方法。俗称main方法,又叫主方法。格式是固定的。 //main方法体 System.out.println("Hello World!"); } } public:被访问的权限。 class:一个特殊的单词。 HelloWorld:类的名字。HelloWorld 要和文件的名字一致。 {}:类体 public static void main(String[] args){}:java 应用程序的入口方法。俗称 main 方法,主方法。格式是固定的。 main方法体: System.out.println("Hello World!"); 在系统的标准输出设备上打印双引号内部的内容。原样输出字符串的内容。并输出一个换行符。 DOS运行过程: 编译:javac HelloWorld.java--->HelloWorld.class 解释:java HelloWorld 为何不添加.class? 因为程序会从包含入口的类开始执行。所以只写类的名字即可。 四、进制: 1:在java 程序中 表示一个具体的数的时候。可以使用几种进制来表示? 四种:二进制、八进制、十进制、十六进制。 //练习java 中程序的进制的表示形式 public class TestNumber{ // 入口方法 main public static void main(String[] args){ //十进制表示10 将数字10 转换为数字对应的字符串,然后再打印。 System.out.println(10); //二进制表示10 高位添加0b 表示这是一个二进制的数据 jdk 1.7增加的功能 System.out.println(0b1010); //八进制 高位补0 代表是一个八进制的数据 System.out.println(012); //十六进制 高位补0x System.out.println(0xa); } } 2:二进制: 规则:逢二进一,一个二进制位可以有2种不同的状态。[0-1] 在计算机学科中,一个二进制位一个0 或者是一个1 是 计算机的最小的存储单元。 一个二进制位称之为1bit。 8bits 称之为一个字节 1byte。 可以有256(2^8)种状态。 0 1(一个二进制位的两种状态2^1) 00 01 10 11(两个二进制位的四种状态2^2) 000 001 010 011 100 101 110 111(三个二进制位的八种状态2^3) 3:十进制:用0-9 十个字符代表10种不同状态。多个十进制的位的组合来代表更多的不同的状态。 规则:逢十进一,一个十进制位可以有10种不同的表示形式[0-9]. 4:八进制: 规则:逢八进一,一个八进制位表示的范围[0-7] 5:十六进制: 规则:逢十六进一,一个十六进制为表示的范围是[0-15],因必须只使用一个字符来表示10~15。所以规定,使用[a-f][A-F]来代表 [10-15] 使用四种进制表示0-15 十进制二进制八进制十六进制 0 0 0 0 1 1 1 1 2 1022 3 11 3 3 4 100 4 4 5 101 5 5 6 110 6 6 7 11177 8100010 8 9100111 9 101010 12A 11 1011 13B 12110014C 13110115D 14111016E 15111117 F 6:进制转换: 1:任何进制到 十进制的转换 123 = 1* 10^2 + 2*10^1 + 3*10^0 = 1*100 + 2 * 10 + 3 * 1 = 100 + 20 + 3 = 123; 0b101 = 1*2^2 + 0 * 2^1 + 1 * 2^0 0715 = 7*8^2+1*8^1+ 5 = 448+13 = 461; 0xa3 = 10 * 16^1+ 3*16^0= 163 2: 十进制到其他进制的转换。 十进制到二进制:除二取余法。逆序输出。 十进制到八进制:除八取余数 十进制到十六进制:除十六取余数 3.八进制和二进制规则:一个八进制位表示成3个二进制位,3个二进制位合成一个八进制位 101010<--->52 61<--->110001 4.十六进制和二进制的转换 规则:一个十六进制位表示成4个二进制位,4个二进制位合成一个十六进制位 0xae3<--->101011100011 五、机器码转码:原码--反码--补码 问:一个整数在内存中是以什么样的形式表示的? 答:首先内存中的表现形式计算机肯定能直接识别。这种表现的形式称之为机器码。 用一个字节来表示机器码。 计算机科学中规定:用字节的最高位来代表符号位,0 代表是正数 1 代表负数。 A、原码机器码的表示数据的规则: 最高位为符号位,低7位为数值位。 10: 0000 1010 -10:1000 1010 计算机并不是使用原码来表示数据的。 缺点:1:有+0 和 -0 之分。 0000 0000 1000 0000 2:如果考虑符号位参与运算,那么在设计cpu的时候,会增加cpu的设计的难度。 B、反码: 规则:如果是正数,那么原码和反码一致。 负数,在原码的基础上,符号位不变,其他的二进制位1 变0 0 变 1 取反。 10:0000 1010 --反码--> 0000 1010 -10:1000 1010--反码--> 1111 0101 C、补码: 规则:如果是正数,那么补码和原码一致。三码合一。负数,先求原码,然后求反码,最后+1. -10: 1000 1010--反码--> 1111 0101 --求补-->(+1) 1111 0110 -1:1000 0001--->1111 1110 ---> 1111 1111 优点: 1:没有+0 和 -0 之分 0000 0000 1000 0000-->1111 1111--(+1)-->1 0000 0000 2:计算机在使用补码计算的时候,并没有把符号位当作符号位来看待,而是当作了普通的数值位来使用,计算的规则和数值位的规则一致。cpu 的设计很简单了。 计算机科学中广泛了采用了补码的形式表示数据。 规则:补码的补码是原码。 1111 1111 ---> 1000 0000 --(+1)-->1000 0001 六、标识符 写程序的目的:数据的运算和处理。 那么在程序中如何去访问要处理的数据呢? 通常的做法是,给要处理的数据所在的内存起一个名字。通过名字来访问数据所在的内存区域。而这个内存区域的名字在java中就称之为:标识符。 一个标识符对应着内存中的一块区域。 1.基本的命名规范: 1:见名知意通过标识符的名字要能体现标识符代表的内存区域的数据的性质。 举个栗子:年龄的数据:age 名字的数据:name 2:遵循驼峰命名法:多个单词构成标识符,单词的首字符要大写。 举个栗子:学生的年龄:studentAge。 stuAge。 3:尽量用英文单词命名。 不同种类的标识符的命名规范: 1.class 后的名字即类名: 首字符大写,遵循基本命名规范即可。 2.局部变量的命名规范: 首字符小写。 尽量不要使用单个字符作为变量名。 3.终态变量的命名规范: 所有字符全部大写,多个单词使用下划线分割。 2.标识符的命名规则: 1:首字符必须 26个英文字母的大小写形式、下划线、$。后面的字符多了[0-9]10个数字字符。 2:不能使用 java 语言中的关键字 (java 中具有特殊意义的单词符号) 。又称为保留字。 Public、class、static、void、final 等 3:标识符大小写敏感。即区分大小写。 4:标识符的长度没有限制。(不建议使用单个字符作为标识符,也不建议太长)。 5:不能使用空格,#等特殊字符。 例子:2name,a,nianling,你好,public,name.

资源下载

更多资源
Nacos

Nacos

Nacos /nɑ:kəʊs/ 是 Dynamic Naming and Configuration Service 的首字母简称,一个易于构建 AI Agent 应用的动态服务发现、配置管理和AI智能体管理平台。Nacos 致力于帮助您发现、配置和管理微服务及AI智能体应用。Nacos 提供了一组简单易用的特性集,帮助您快速实现动态服务发现、服务配置、服务元数据、流量管理。Nacos 帮助您更敏捷和容易地构建、交付和管理微服务平台。

Rocky Linux

Rocky Linux

Rocky Linux(中文名:洛基)是由Gregory Kurtzer于2020年12月发起的企业级Linux发行版,作为CentOS稳定版停止维护后与RHEL(Red Hat Enterprise Linux)完全兼容的开源替代方案,由社区拥有并管理,支持x86_64、aarch64等架构。其通过重新编译RHEL源代码提供长期稳定性,采用模块化包装和SELinux安全架构,默认包含GNOME桌面环境及XFS文件系统,支持十年生命周期更新。

Sublime Text

Sublime Text

Sublime Text具有漂亮的用户界面和强大的功能,例如代码缩略图,Python的插件,代码段等。还可自定义键绑定,菜单和工具栏。Sublime Text 的主要功能包括:拼写检查,书签,完整的 Python API , Goto 功能,即时项目切换,多选择,多窗口等等。Sublime Text 是一个跨平台的编辑器,同时支持Windows、Linux、Mac OS X等操作系统。

WebStorm

WebStorm

WebStorm 是jetbrains公司旗下一款JavaScript 开发工具。目前已经被广大中国JS开发者誉为“Web前端开发神器”、“最强大的HTML5编辑器”、“最智能的JavaScript IDE”等。与IntelliJ IDEA同源,继承了IntelliJ IDEA强大的JS部分的功能。

用户登录
用户注册