首页
留言板
友链
关于
Search
1
Apache Dubbo初步认识
254 阅读
2
内测“合金弹头”嘿嘿
248 阅读
3
Nginx搭建一个简易的图床
237 阅读
4
给站点添加邮件通知功能
215 阅读
5
“前端防呆”——持续更新
129 阅读
知识库
好奇猫
日常说
笔记本
登录
用户名
密码
登 录
Search
标签搜索
maven
Java代码
帆软
Java
git
前端
CSS
游戏
idea
Bootstrap
生活
网上冲浪
邮件配置
说说
vue
Nginx
Excel
MySQL
数据库调优
显卡
龙流
累计撰写
37
篇文章
累计收到
10
条评论
首页
栏目
知识库
好奇猫
日常说
笔记本
页面
留言板
友链
关于
搜索到
37
篇与
龙流
的结果
2022-04-19
本地项目上传到”码云“
登录"码云" 登录或注册码云,进入主页后点击+号新建仓库,如下图为避免error: failed to push some refsto‘远程仓库地址’的错误不要勾选“使用Readme文件初始化这个仓库”根据向导,可直接点击立即创建即可.使用git应用 未安装git的可参考此文章.命令行模式下,进行要上传到git的目录下:初始化本地仓库git命令:git init上传他人拷贝给自己的项目时,拷贝过来的文件夹中如果有.git目录(注意此文件夹是隐藏文件夹)使用命令关联远端仓库git remote add origin 远端仓库地址查看当前配置信息git config --list 使用git status命令查看仓库文件状态-s 选项——精简输出具体参考第一列字符表示版本库与暂存区之间的比较状态。第二列字符表示暂存区与工作区之间的比较状态。' ' (空格)表示文件未发生更改M 表示文件发生改动。A 表示新增文件。D 表示删除文件。R 表示重命名。C 表示复制。U 表示更新但未合并。? 表示未跟踪文件。! 表示忽略文件。未跟踪和忽略文件会显示相同的两列,如 ??。先将修改过的文件添加到暂存区域,执行添加指令git add .再将暂存区域的文件,提交至本地仓库,执行提交指令git commit -m"commit msg"最后将本地仓库推送至远端仓库,执行推送指令git push origin master注意:如果用户信息输入有误,会提示 remote: Invalid username or password.再次执行推送指令,即可重新填写用户信息。了解:当本地仓库与远程仓库不一致时,推送并合并分支git pull --rebase origin mastergit pull origin master【Git】git pull origin master与git pull --rebase origin master的区别:git pull=git fetch + git mergegit pull --rebase=git fetch+git rebasegit fetch : 从远程分支拉取代码,可以得到远程分支上最新的代码。所以git pull origin master与git pull --rebase origin master的区别主要是在远程与本地代码的合并上面了。对比可看出:git merge多出了一个新的节点G,会将远端master的代码和test本地的代码在这个G节点合并,之前的提交会分开去显示。git --rebase会将两个分支融合成一个线性的提交,不会形成新的节点。rebase好处想要更好的提交树,使用rebase操作会更好一点。这样可以线性的看到每一次提交,并且没有增加提交节点。merge 操作遇到冲突的时候,当前merge不能继续进行下去。手动修改冲突内容后,add 修改,commit 就可以了。而rebase 操作的话,会中断rebase,同时会提示去解决冲突。解决冲突后,将修改add后执行git rebase –continue继续操作,或者git rebase –skip忽略冲突。
2022年04月19日
18 阅读
0 评论
0 点赞
2022-04-18
IBM MQ使用,传递消息
参考连接Jquery发送ajax请求get $.get("[后台请求地址]",{},[回调方法]function(data[返回的数据data]){ //业务处理。。。 }); $.get("category/findAll",{},function(data){} var lis = '首页'; for(var i = 0; i < data.length; i++) { //动态拼接 var li = '+ data[i].cname +'; lis += li; } //拼接尾部 lis += '收藏' //将lis字符串输出到标签中 $(#category).html(lis); );
2022年04月18日
14 阅读
0 评论
0 点赞
2022-04-07
Spring集成web环境 SpringMVC
Spring提供获取应用上下文的工具1、分析: Spring提供了一个监听器ContextLoaderListener,上面的分析不用手动实现,该监听器内部加载Spring配置文件,创建应用上下文对象,并存储到ServletContext域中。 Spring提供了一个客户端工具WebApplicationContextUtils供使用者获得应用上下文对象。2、使用: 我们需要做两件事①在web.xml中配置ContextLoaderListener监听器(导入spring-web坐标)②使用WebApplicationContextUtils获得应用上下文对象ApplicationContext3、实例:<!--导入spring-web坐标--> <dependency> <groupId>org.springframework</groupId> <artifactId>spring-web</artifactId> <version>5.0.5.RELEASE</version> </dependency>配置web.xml<!--全局初始化参数--> <context-param> <param-value>classpath:applicationContext.xml</param-value> </context-param> <!--配置监听器--> <listener> <listener-class>org.springframework.web.context.ContextLoaderListener</1istener-class> </listener>配置算是完成了开始使用:ApplicationContext applicationContext = WebApplicationContextUtils.getWebApplicationContext(servletContext); Object obj = applicationContext.getBean ("id"); SpringMVC 统一管理处理Servlet1、分析: SpringMVC通过DispatcherServlet全局的Servlet称之为前端控制器,根据请求资源地址然后映射到具体的Controller。 视图和模型(ModelAndView),每个Controller中的每个方法根据需求进行返回,可以只返回文字信息,可以返回视图模型信息。①用户发送请求至前端控制器DispatcherServlet。②DispatcherServlet收到请求调用HandlerMapping处理器映射器。③处理器映射器找到具体的处理器(可以根据xml配置、注解进行查找),生成处理器对象及处理器拦截器(如果有则生成)一并返回给DispatcherServlet。④DispatcherServlet调用HandlerAdapter处理器适配器。⑤HandlerAdapter经过适配调用具体的处理器(Controller,也叫后端控制器)。⑥Controller执行完成返回ModelAndView。⑦HandlerAdapter将controller执行结果ModelAndView返回给DispatcherServlet。⑧DispatcherServlet将ModelAndView传给ViewReslover视图解析器。⑨ViewReslover解析后返回具体View。⑩DispatcherServlet根据View进行渲染视图(即将模型数据填充至视图中)。DispatcherServlet响应用户。2、使用:①导入SpringMVC相关坐标②web.xml配置SpringMVC核心控制器DispathcerServlet③配置SpringMVC核心文件 spring-mvc.xml④创建Controller类和视图页面⑤使用注解配置Controller类中业务方法的映射地址⑥客户端发起请求测试
2022年04月07日
12 阅读
0 评论
0 点赞
2022-03-27
Redis
redis的5种数据类型:string 字符串(可以为整形、浮点型和字符串,统称为元素)hash hash散列值(hash的key必须是唯一的)list 列表(实现队列,元素不唯一,先入先出原则)set 集合(各不相同的元素)sort set 有序集合string类型的常用命令:添加:set获取:get自加:incr自减:decr加: incrby减: decrbylist类型支持的常用命令:lpush:从左边推入lpop:从右边弹出rpush:从右变推入rpop:从右边弹出llen:查看某个list数据类型的长度set类型支持的常用命令:sadd:添加数据scard:查看set数据中存在的元素个数sismember:判断set数据中是否存在某个元素srem:删除某个set数据中的元素hash数据类型支持的常用命令:hset:添加hash数据hget:获取hash数据hmget:获取多个hash数据sort set和hash很相似,也是映射形式的存储:zadd:添加zcard:查询zrange:数据排序Redis的持久化redis持久化机制: 1、RDB:默认方式,不需要进行配置,默认就使用这种机制 在一定的间隔时间中,检测key的变化情况,然后持久化数据 redis安装目录下的配置文件:redis.windows.conf #900秒(15min)之后如果有一个Key发生改变就进行一次持久化 save 900 1 #300秒(5min)之后如果有10个Key发生改变就进行一次持久化 save 300 10 #60秒(1min)之后如果有10000个Key发生改变就进行一次持久化 save 60 10000 2、AOF:日志记录的方式,可以记录每一条命令的操作。可以每一次命令操作后,持久化数据使用配置的启动方式:命令行 redis-server.exe redis.windows.confredis缓存操作eg:缓存一些数据库中不宜发生改变的数据,如省份信息;将数据库中的省份信息以JSON字符串的形式保持在缓存中,每次查询时先查缓存,没有再去查询数据库 注意点:要保证redis数据与数据库的一直性,对数据库中的省份信息进行增删改操作时,要更新缓存。
2022年03月27日
20 阅读
0 评论
0 点赞
2022-03-25
javax.servlet.Filter与动态代理
Java过滤器 Fileter快速入门:1、定义一个类,实现**javax.servlet.Filter**接口 2、重写其3个方法,init(); doFilter(); destroy(); 3、配置拦截资源: 通过web.xml配置 注解配置 @WebFilter("urlPatterns") web.xml<?xml version="1.0" encoding="UTF-8"?> <web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_3_1.xsd" version="3.1"> <filter> <filter-name>demo1</filter-name> <filter-class>cn.itcast.web.filter.FilterDemo1</filter-class> </filter> <filter-mapping> <filter-name>demo1</filter-name> <!-- 拦截路径 --> <url-pattern>/*</url-pattern> </filter-mapping> </web-app>Filter实现代码public class FilterDemo implements Filter{ @Override public void init(FilterConfig filterConfig) throws ServletException { } @Override public void doFilter(ServletRequest servletRequest, ServletResponse servletResponse, FilterChain filterChain) throws IOException, ServletException { System.out.println("filterDemo1被执行了...."); //放行 filterChain.doFilter(servletRequest,servletResponse); } @Override public void destroy() {} }过来器生命周期1、init()方法,会在Filter对象创建时执行一次。一般可以用来加载资源2、doFilter()方法,会执行多次,符合拦截规则的请求,在请求到资源前都会执行该方法,filterChain.doFilter(servletRequest,servletResponse)放行,拿到资源后,即进行响应时会走放行后的代码3、destroy()方法,会在Filter对象被正常消耗时会执行一次。过滤器链(配置多个过滤器) * 执行顺序:如果有两个过滤器:过滤器1和过滤器2 1. 过滤器1 2. 过滤器2 3. 资源执行 4. 过滤器2 5. 过滤器1 * 过滤器先后顺序问题: 1. 注解配置:按照类名的字符串比较规则比较,值小的先执行 * 如: AFilter 和 BFilter,AFilter就先执行了。 2. web.xml配置: <filter-mapping>谁定义在上边,谁先执行 使用动态代理增强Filter过滤器中的doFilter(servletRequest,servletResponse)方法的servletRequest对象的getParameter()方法动态代理 * 概念: 1. 真实对象:被代理的对象 2. 代理对象: 3. 代理模式:代理对象代理真实对象,达到增强真实对象功能的目的 * 实现方式: 1. 静态代理:有一个类文件描述代理模式 2. 动态代理:在内存中形成代理类 * 实现步骤: 1. 代理对象和真实对象实现相同的接口 2. 代理对象 = Proxy.newProxyInstance(); 3. 使用代理对象调用方法。 4. 增强方法 * 增强方式: 1. 增强参数列表 2. 增强返回值类型 3. 增强方法体执行逻辑 @Override public void doFilter(ServletRequest servletRequest,ServletResponse servletResponse,FilterChain filterChain) throws IOException, ServletException { //1、创建代理对象,增强getParameter()方法 ServletRequest request_proxy = (ServletRequest)Proxy.newProxyInstance(servletRequest.getClass().getClassLoader(), servletRequest.getClass().getInterfaces(), new InvokeHandler(){ @Override public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { //要增强getParameter(),所以进行个判断 if("getParameter".equals(method.getName())){ //增强返回值,对request对象值请求参数中的值进行个修改 String parameter = (String)method.invoke(servletRequest,args); //进行修改然后返回 return parameter.replace("#",""); }else { return method.invoke(servletRequest,args); } } }); //2、放行 filterChain.doFilter(); }
2022年03月25日
8 阅读
0 评论
0 点赞
2022-03-24
java.lang.String的substring、split方法引起的内存问题
项目运行遇到了OutOfMemoryError异常.内存溢出?觉得是不是MaxPermSize设置小了,又给了1个G的大小,试下还是不行,然后使用Java heap分析工具,找出内存占用超出预期的嫌疑对象dump heapHeap Dump也叫堆转储文件,是一个Java进程在某个时间点上的内存快照。Heap Dump是有着多种类型的。不过总体上heap dump在触发快照的时候都保存了java对象和类的信息。通常在写heap dump文件前会触发一次FullGC,所以heap dump文件中保存的是FullGC后留下的对象信息。关于Heap Dump使用jconsole获取dump heap:建立连接后,选择页签MBean,执行com.sun.management. HotSpotDiagnostic下的操作dumpHeap。第一个参数p0是要获取的dump文件的完整路径名,记得文件要以.hprof作为扩展名(要在Memory AnalysisPerspective下打开扩展名必须是这个)。如果我们只想获取live的对象,第二个参数p1需要保持为true。JDK自带的jmap工具:Java代码jmap -dump:format=b,file=heap.bin <pid> format=b的含义是,dump出来的文件时二进制格式。 file-heap.bin的含义是,dump出来的文件名是heap.bin。 <pid>就是JVM的进程号。 (在linux下)先执行ps aux | grep java,找到JVM的pid;然后再执行jmap -dump:format=b,file=heap.bin <pid>,得到heap dump文件。analyze heap将二进制的heap dump文件解析成human-readable的信息,自然是需要专业工具的帮助,Memory Analyzer Memory Analyzer,简称MAT,是Eclipse基金会的开源项目,由SAP和IBM捐助。巨头公司出品的软件还是很中用的,MAT可以分析包含数亿级对 象的heap、快速计算每个对象占用的内存大小、对象之间的引用关系、自动检测内存泄露的嫌疑对象,功能强大,而且界面友好易用。 MAT的界面基于Eclipse开发,以两种形式发布:Eclipse插件和Eclipe RCP。MAT的分析结果以图片和报表的形式提供,一目了然。{gird column="2" gap="15"}{gird-item}{/gird-item}{gird-item}{/gird-item}{/gird}最后发现到这里内存突然就爆增看到这里我就觉得是不是split()使用的有什么问题于是就上网查了一下split学习了一下。原文连接:https://blog.csdn.net/caihaijiang/article/details/7748560先用一个极端例子说明String的substring方法引起的OutOfMemoryError问题:public class TestGC { private String large = new String(new char[100000]); public String getSubString() { return this.large.substring(0,2); } public static void main(String[] args) { ArrayList<String> subStrings = new ArrayList<String>(); for (int i = 0; i <1000000; i++) { TestGC testGC = new TestGC(); subStrings.add(testGC.getSubString()); } } }:对一个很长的字符串,使用substring循环保留该字符串里面的一小部分,保存到HashMap中运行该程序,结果出现:Exception in thread "main" java.lang.OutOfMemoryError: Java heap space为什么会出现这个情况?查看一下JDK String类substring方法的源码,可以找到原因,源码如下: public String substring(int beginIndex, int endIndex) { if (beginIndex < 0) { throw new StringIndexOutOfBoundsException(beginIndex); } if (endIndex > count) { throw new StringIndexOutOfBoundsException(endIndex); } if (beginIndex > endIndex) { throw new StringIndexOutOfBoundsException(endIndex - beginIndex); } return ((beginIndex == 0) && (endIndex == count)) ? this : new String(offset + beginIndex, endIndex - beginIndex, value); }该方法最后一行,调用了String的一个私有的构造方法,如下: // Package private constructor which shares value array for speed. String(int offset, int count, char value[]) { this.value = value; this.offset = offset; this.count = count; }该方法为了避免内存拷贝,提高性能,并没有重新创建char数组,而是直接复用了原String对象的char[],通过改变偏移量和长度来标识不同的字符串内容。也就是说,substring出的来String小对象,仍然会指向原String大对象的char[],所以就导致了OutOfMemoryError问题。找到问题之后,将上面代码中,getSubString的方法修改一下,如下: public String getSubString() { return new String(this.large.substring(0,2)); }将substring的结果,重新new一个String出来。再运行该程序,则没有出现OutOfMemoryError的问题。为什么?因为此时调用的是String类的public的构造方法,该方法源码如下: public String(String original) { int size = original.count; char[] originalValue = original.value; char[] v; if (originalValue.length > size) { // The array representing the String is bigger than the new // String itself. Perhaps this constructor is being called // in order to trim the baggage, so make a copy of the array. int off = original.offset; v = Arrays.copyOfRange(originalValue, off, off+size); } else { // The array representing the String is the same // size as the String, so no point in making a copy. v = originalValue; } this.offset = 0; this.count = size; this.value = v; }从代码可以看出,在String对象中value的length大于count的情况下,会重新创建一个char[],并进行内存拷贝。除了substring方法之后,String的split方法,也存在同样的问题,split的源码如下: public String[] split(String regex, int limit) { return Pattern.compile(regex).split(this, limit); }可以看出,String的split方法通过Pattern的split方法来实现,Pattern的split方法源码如下:public String[] split(CharSequence input, int limit) { int index = 0; boolean matchLimited = limit > 0; ArrayList<String> matchList = new ArrayList<String>(); Matcher m = matcher(input); // Add segments before each match found while(m.find()) { if (!matchLimited || matchList.size() < limit - 1) { String match = input.subSequence(index, m.start()).toString(); matchList.add(match); index = m.end(); } else if (matchList.size() == limit - 1) { // last one String match = input.subSequence(index, input.length()).toString(); matchList.add(match); index = m.end(); } } // If no match was found, return this if (index == 0) return new String[] {input.toString()}; // Add remaining segment if (!matchLimited || matchList.size() < limit) matchList.add(input.subSequence(index, input.length()).toString()); // Construct result int resultSize = matchList.size(); if (limit == 0) while (resultSize > 0 && matchList.get(resultSize-1).equals("")) resultSize--; String[] result = new String[resultSize]; return matchList.subList(0, resultSize).toArray(result); }方法中的第9行: Stirng match = input.subSequence(intdex, m.start()).toString();调用了String类的subSequence方法,该方法源码如下: public CharSequence subSequence(int beginIndex, int endIndex) { return this.substring(beginIndex, endIndex); }通过代码可以看出,最终调用的是String类的substring方法,因此存在同样的问题。split出来的小对象,直接使用原String对象的char[]。看了一下StringBuilder和StringBuffer的substring方法,则不存在这样的问题。其源码如下: public String substring(int start, int end) { if (start < 0) throw new StringIndexOutOfBoundsException(start); if (end > count) throw new StringIndexOutOfBoundsException(end); if (start > end) throw new StringIndexOutOfBoundsException(end - start); return new String(value, start, end - start); }最后一行,调用了String类的public构造方法,方法源码如下: public String(char value[], int offset, int count) { if (offset < 0) { throw new StringIndexOutOfBoundsException(offset); } if (count < 0) { throw new StringIndexOutOfBoundsException(count); } // Note: offset or count might be near -1>>>1. if (offset > value.length - count) { throw new StringIndexOutOfBoundsException(offset + count); } this.offset = 0; this.count = count; this.value = Arrays.copyOfRange(value, offset, offset+count); }方法不是直接使用原String对象的char[],而是重新进行了内存拷贝。
2022年03月24日
16 阅读
0 评论
0 点赞
2022-03-23
序列化与反序列化
把对象转换为字节序列的过程称为对象的序列化。把字节序列恢复为对象的过程称为对象的反序列化。对象的序列化主要有两种用途:把对象的字节序列永久地保存到硬盘上,通常存放在一个文件中;在网络上传送对象的字节序列。使用场景 在很多应用中,需要对某些对象进行序列化,让它们离开内存空间,入住物理硬盘,以便长期保存。比如最常见的是Web服务器中的Session对象,当有 10万用户并发访问,就有可能出现10万个Session对象,内存可能吃不消,于是Web容器就会把一些session先序列化到硬盘中,等要用了,再把保存在硬盘中的对象还原到内存中。 当两个进程在进行远程通信时,当我们需要使用的对象很复杂或者需要很长时间去构造,这时就会引入使用代理模式(Proxy)。例如:如果构建一个对象很耗费时间和计算机资源,代理模式(Proxy)允许我们控制这种情况,直到我们需要使用实际的对象。一个代理(Proxy)通常包含有和将要使用的对象同样的方法,一旦开始使用这个对象,这些方法将通过代理(Proxy)传递给实际的对象。解读:在微服务化盛行的今天,很多复杂的对象构造起来比较耗时,为了节省开支,某些公司将这部分复杂的对象先圈起来,写成服务起在远端B,并在调用端A端以代理(Proxy)的形式提供对服务的访问,这期间从B到A远程调的过程形成了Java对象序列化和反序列化的相关操作!深入理解JDK提供了个代理类:import java.lang.reflect.Proxy; 来看一下Proxy的实现Proxy在JDK中实现了Serializable(序列化)接口,但是代理是怎么实现将服务端的对象运行到客户端上的呢?第一步:远端JVM(服务端)对“对象”使用序列化后通过网络传输的方式将对象的字节序列发送到本地(客户端)第二步:本地代理(Proxy)将接收到的字节序列再通过反序列化恢复成”对象”,并使这个"对象"活在本地的JVM中;从上面两步来看,序列化的过程是在服务端做的;反序列化是在客户端做的;那么有个问题来了,从源码上看,Proxy(本地)实现了反序列化,服务端在哪里实现了序列化呢?带着这个问题,继续查看了服务端所有的代码,发现有的公司直接在实体上Serialize,有的则在类上加@Serializable注解利用切面实现,但终究实现了序列化;JDK类库中的序列化APIjava.io.ObjectOutputStream代表对象输出流,它的writeObject(Object obj)方法可对参数指定的obj对象进行序列化,把得到的字节序列写到一个目标输出流中。java.io.ObjectInputStream代表对象输入流,它的readObject()方法从一个源输入流中读取字节序列,再把它们反序列化为一个对象,并将其返回。 只有实现了Serializable和Externalizable接口的类的对象才能被序列化。Externalizable接口继承自 Serializable接口,实现Externalizable接口的类完全由自身来控制序列化的行为,而仅实现Serializable接口的类可以 采用默认的序列化方式 。对象序列化/** * @ClassName: Student * @author: WeiLong * @description 学生类 */ public class Student implements Serializable{ private String name; private int age; public Student(String name, int age) { this.name = name; this.age = age; } public String getName() { return name; } public void setName(String name) { this.name = name; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } }注意:需要序列化的类需要实现Serializable接口,获取上述其他方法,否则会出现NotSerializableException //实现序列化 public static void SerializeStudent() throws IOException{ //1、序列化流 ObjectOutputStream oos = new ObjectOutputStream(new FileOutputStream("java基础\\oos.txt")); //2、创建student对象 Student student = new Student("张三",18); oos.writeObject(student); System.out.println("Student对象序列化成功"); //3、关闭流 oos.close(); }对象反序列化 public static Student DeserializeStudent() throws IOException, ClassNotFoundException { ObjectInputStream ois = new ObjectInputStream(new FileInputStream("java基础\\oos.txt")); Student student = (Student) ois.readObject(); System.out.println("Student对象反序列化完成"); ois.close(); return student; }serialVersionUID的作用序列化对象未加serialVersionUID时,反序列化异常serialVersionUID: 字面意思上是序列化的版本号,凡是实现Serializable接口的类都有一个表示序列化版本标识符的静态变量 序列化版本ID的真实用途:当实体中增加属性后,文件流中的class和classpath中的class,也就是修改过后的class,不兼容了,处于安全机制考虑,程序抛出了错误,并且拒绝载入。那么如果我们真的有需求要在序列化后添加一个字段或者方法呢?应该怎么办?那就是自己去指定serialVersionUID。在例子中,如果没有指定Person类的serialVersionUID的,那么java编译器会自动给这个class进行一个摘要算法,类似于指纹算法,只要这个文件多一个空格,得到的UID就会截然不同的,可以保证在这么多类中,这个编号是唯一的。所以,添加了一个字段后,由于没有显指定serialVersionUID,编译器又为我们生成了一个UID,当然和前面保存在文件中的那个不会一样了,于是就出现了2个序列化版本号不一致的错误。因此,只要我们自己指定了serialVersionUID,就可以在序列化后,去添加一个字段,或者方法,而不会影响到后期的还原,还原后的对象照样可以使用,而且还多了方法或者属性可以用。可以说serialVersionUID是序列化和反序列化之间彼此认识的唯一信物。IDEA设置自动生成serialVersionUID
2022年03月23日
15 阅读
0 评论
0 点赞
2022-03-21
实际使用数据库建表时varchar类型使用的区别
一直有纠结过一个问题,不知道其他同学有没有,实际开发中数据库字段类型varchar类型是最好用的,不会出现类型不符的异常;管它是int类型还是double、date类型都能当字符占用类型存储。这真的是一个“坏味道”要改一改了:1、从字段类型的执行效率上,int最高,varchar最低。例如状态类型字段,使用char或者varchar是不可取的,int类型更容易建立索引和进行检索,毕竟数字类型是数据库检索的基础,char类型的毕竟需要经过转换,而varchar就更复杂了,其排序不仅需要转换和计算,还需要访问和遵循数据库的排序规则(实际上char也需要排序规则),而消耗的资源也更大。2、占用空间大小不同例如用varchar 来存储年月日,那么需要10个字节,而date类型只需4个字节,而datetime类型也只需要8个字节,都小于varchar类型3、建立索引问题频繁改动的字段类型建议使用varchar。varchar类型的字段用来建立索引容易产生的索引碎片简单理解索引碎片是空间利用率不足SQL索引与碎片=>删除索引并重建这种方式并不好.在删除索引期间,索引不可用.会导致阻塞发生。而对于删除聚集索引,则会导致对应的非聚集索引重建两次(删除时重建,建立时再重建).虽然这种方法并不好,但是对于索引的整理最为有效=>使用DROP_EXISTING语句重建索引为了避免重建两次索引,使用DROP_EXISTING语句重建索引,因为这个语句是原子性的,不会导致非聚集索引重建两次,但同样的,这种方式也会造成阻塞=>如前面文章所示,使用ALTER INDEX REBUILD语句重建索引使用这个语句同样也是重建索引,但是通过动态重建索引而不需要卸载并重建索引.是优于前两种方法的,但依旧会造成阻塞。可以通过ONLINE关键字减少锁,但会造成重建时间加长.
2022年03月21日
20 阅读
0 评论
0 点赞
2022-02-23
实例volatile关键字使用场景 了解volatile关键字作用
volatile用volatile修饰的变量,线程在每次使用变量的时候,都会读取变量修改后的最新的值。volatile很容易被误用,用来进行原子性操作。如果要深入了解volatile关键字的作用,就必须先来了解一下JVM在运行时候的内存分配过程。在 java 垃圾回收整理一文中,描述了jvm运行时刻内存的分配。其中有一个内存区域是jvm虚拟机栈,每一个线程运行时都有一个线程栈,线程栈保存了线程运行时候变量值信息。当线程访问某一个对象值的时候,首先通过对象的引用找到对应在堆内存的变量的值,然后把堆内存变量的具体值load到线程本地内存中,建立一个变量副本,之后线程就不再和对象在堆内存变量值有任何关系,而是直接修改副本变量的值,在修改完之后的某一个时刻(线程退出之前),自动把线程变量副本的值回写到对象在堆中变量。这样在堆中的对象的值就产生变化了。下面一幅图:以上是JVM在运行时候的内存分配过程以后,我们开始真正深入的讨论volatile的具体作用public class VolatileTest extends Thread { boolean flag = false; int i = 0; public void run() { while (!flag) { i++; } } public static void main(String[] args) throws Exception { VolatileTest vt = new VolatileTest(); vt.start(); Thread.sleep(2000); vt.flag = true; System.out.println("stope" + vt.i); } }上面的代码是通过标记flag来控制VolatileTest线程while循环退出的例子!下面让我用伪代码来描述一下我们的程序首先创建 VolatileTest vt = new VolatileTest();然后启动线程 vt.start();暂停主线程2秒(Main) Thread.sleep(2000);这时的vt线程已经开始执行,进行i++;主线程暂停2秒结束以后将 vt.flag = true;打印语句 System.out.println("stope" + vt.i); 在此同时由于vt.flag被设置为true,所以vt线程在进行下一次while判断 while (!flag) 返回假 结束循环 vt线程方法结束退出!主线程结束上面的叙述看似并没有什么问题,“似乎”完全正确。那就让我们把程序运行起来看看效果吧,执行mian方法。2秒钟以后控制台打印stope-202753974。可是奇怪的事情发生了 程序并没有退出。vt线程仍然在运行,也就是说我们在主线程设置的 vt.flag = true;没有起作用。 说明:有的同学在测试上面代码的时候程序可以正常退出。那是因为你的JVM没有优化造成的。在DOC下面输入 java -version 查看 如果显示Java HotSpot(TM) ... Server 则JVM会进行优化 如果显示Java HotSpot(TM) ... Client 为客户端模式,需要设置成Server模式 设置方法问Google问题出现了,为什么我在主线程(main)中设置了vt.flag = true; 而vt线程在进行判断flag的时候拿到的仍然是false?那么按照我们上面所讲的 “JVM在运行时候的内存分配过程” 就很好解释上面的问题了。 首先 vt线程在运行的时候会把 变量 flag 与 i (代码3,4行)从“主内存” 拷贝到 线程栈内存(上图的线程工作内存) 然后 vt线程开始执行while循环while (!flag) { i++ }while (!flag)进行判断的flag 是在线程工作内存当中获取,而不是从 “主内存”中获取。i++; 将线程内存中的i++; 加完以后将结果写回至 "主内存",如此重复。然后再说说主线程的执行过程。 我只说明关键的地方vt.flag = true;主线程将vt.flag的值同样 从主内存中拷贝到自己的线程工作内存 然后修改flag=true. 然后再将新值回到主内存。这就解释了为什么在主线程(main)中设置了vt.flag = true; 而vt线程在进行判断flag的时候拿到的仍然是false。那就是因为vt线程每次判断flag标记的时候是从它自己的“工作内存中”取值,而并非从主内存中取值!这也是JVM为了提供性能而做的优化。那我们如何能让vt线程每次判断flag的时候都强制它去主内存中取值呢。这就是volatile关键字的作用。再次修改我们的代码public class VolatileTest extends Thread { volatile boolean flag = false; int i = 0; public void run() { while (!flag) { i++; } } public static void main(String[] args) throws Exception { VolatileTest vt = new VolatileTest(); vt.start(); Thread.sleep(2000); vt.flag = true; System.out.println("stope" + vt.i); } }在flag前面加上volatile关键字,强制线程每次读取该值的时候都去“主内存”中取值。在试试我们的程序吧,已经正常退出了。原文出处:https://www.cnblogs.com/xd502djj/p/9873067.html作者:懂俊杰
2022年02月23日
23 阅读
0 评论
0 点赞
2022-01-17
反射动态调用某个类某个方法
简单的通过配置文件加反射的方法执行某方法{collapse}{collapse-item label="普通方法反射调用" open}public static void main(String[] args){ /** * eg:txt配置文件class.txt或properties的配置文件 * className=xxx * methodName=xxx */ Properties prop = new Properties(); FileRead fr = new FileRead("MyProject\\class.txt"); prop.load(fr); fr.close(); String className = prop.getProperty("className"); String methodName = prop.getProperty("methodName"); //通过反射来使用 Class<?> c = Class.forName(className); Constructor<?> con = c.getConstructor(); Object obj = con.newInstance(); Method m = c.getMethod(methodName); m.invoke(obj); }{/collapse-item}{collapse-item label="折叠标题二"} 折叠内容二{/collapse-item}{/collapse}
2022年01月17日
14 阅读
0 评论
0 点赞
1
2
...
4