- 👋 Hi, I’m @qingshanyuluo
- 👀 I’m interested in ...
- 🌱 I’m currently learning ...
- 💞️ I’m looking to collaborate on ...
- 📫 How to reach me ...
qingshanyuluo / qingshanyuluo.github.io Goto Github PK
View Code? Open in Web Editor NEWpage
page
mark数组有没有访问过,回溯递归,mark数组有优化的办法,但是很麻烦,算了
两个指针都分别从两条链表上都走过,这样速度就一样了
public ListNode getIntersectionNode(ListNode headA, ListNode headB) {
if (headA == null || headB == null) return null;
ListNode pA = headA, pB = headB;
while (pA != pB) {
pA = pA == null ? headB : pA.next;
pB = pB == null ? headA : pB.next;
}
return pA;
}
for(int i = l; i <= r; ++i) ans.push_back(matrix[u][i]); //向右移动直到最右
if(++ u > d) break; //重新设定上边界,若上边界大于下边界,则遍历遍历完成,下同
for(int i = u; i <= d; ++i) ans.push_back(matrix[i][r]); //向下
if(-- r < l) break; //重新设定有边界
for(int i = r; i >= l; --i) ans.push_back(matrix[d][i]); //向左
if(-- d < u) break; //重新设定下边界
for(int i = d; i >= u; --i) ans.push_back(matrix[i][l]); //向上
if(++ l > r) break; //重新设定左边界
数学证明画图 分环前x 环相遇前y 环相遇后 z,可以证明得 z+(走几圈环)=x,所以相遇后让一个指针从头开始跑就行了
public class Solution {
public ListNode detectCycle(ListNode head) {
ListNode fast = head, slow = head;
while (true) {
if (fast == null || fast.next == null) return null;
fast = fast.next.next;
slow = slow.next;
if (fast == slow) break; //一快一慢,如果碰到就停止
}
fast = head;
while (slow != fast) {
slow = slow.next; // 碰到后另一个从头开始,都以速度1来跑。再碰到就是环开始了
fast = fast.next;
}
return fast;
}
}
动态规划dp数组记录当前位为末尾时最长递增,具体怎么找就是for循环前面的,找比当前位小的数字对应下标的dp[j]加一
最适合的方式是按列求并且动态规划,找出这列左右最高的柱子
先用两个for循环,找出 当前位置左边最高的和右边
for (int i = 1; i < height.length - 1; i++) {
max_left[i] = Math.max(max_left[i - 1], height[i - 1]);
}
for (int i = height.length - 2; i >= 0; i--) {
max_right[i] = Math.max(max_right[i + 1], height[i + 1]);
}
然后再for循环找出两边相对最矮的,减去当前柱子高度就是雨水量
深度遍历,返回值为树从上到下最高的和,左右两个加起来加根节点值即为路径和,注意的是,root的val一定要返回,在计算左右子树最大和的时候负数才会被忽略
双指针,一个多走几步
动态规划,二维数组,dp[i][j] 取 增删 改 三个最小的代价,两层for循环从1开始
两个栈 当出栈为空的时候再将入栈塞过去
除了层序遍历,还可以深度优先 先中后右后左,这样可以保证每一层第一个访问到的是最右的。
先按左边界排序,然后遍历,如果遍历到左边界比结果右边界还大,就新开一个结果
p = q;
q = r;
r = p + q;
搞个新数组,简单办法,非要log(n+m)就是二分,比如找第8大的数组 那两个数组中前4个,相对小的那个肯定能排除
嘻嘻嘻
哈希算法又称摘要算法,是一个多对一的映射,很多典型的例子如MD5,SHA-1
数据库中不能直接存密码的明文,否则一旦数据库被攻破,就可以登陆所有用户的账号,方法是存储用户口令的哈希,例如,MD5。在用户输入原始口令后,系统计算用户输入的原始口令的MD5并与数据库存储的MD5对比,如果一致,说明口令正确,否则,口令错误。
简单的哈希加密并不能有效抵御上一种情况,虽然穷举很低效,但是如果做到一张对应表,那就可以直接算出来明文,为此,可以对明文密码加上一串随机数据,再进行hash算法,这样用表的方式也推不到原始的明文,然鹅,要验证输出的哈希,必须同时提供“认证码”。但是使用随机生成的盐也是有一定的弊端,较大的弊端就是,这个盐也必须存储,所以也是有机会获取的盐需要每个用户唯一,这样可以很大程度上防止查表,因为盐不同,即使知道盐是什么也无法生成数量如此庞大的表。可以使用128位的uuid来作为用户的盐,这样可以保证唯一性也可以保证盐的长度。
但是我个人更建议盐可以使用某种非公开的算法根据用户信息生成,而不是直接存储在数据表中的信息,即盐也需要保密。事实上盐最好也不公开,最简单的办法就是分为两部分,一部分为入参数据使用用户信息,这些存储于数据库,另外一部分使用生成算法,这些固化在代码中,不会随数据库沦陷而泄露。而且可以用盐反复加密,对方不知道你的代码逻辑,也很难破解。
纯window下开发,有很多困难,普通的环境Windows配起来都很是费劲
开始编程的时候大部分都是用这种方式,但由于windows火热的编程时代一去不复返,浏览器和移动端开始占据市场主流,在这个时代,linux的地位空前的高,大多数的后端服务器都运行在Linux平台,与此同时,开源的力量也变得空前的强大,考虑到大多数开源组件都是在Linux下先发布,同时对类unix系统的支持也比较好,例如mysql redis这些在linux比较好的组件就放在linux下运行,然后通过网络和本地windows的程序连接,达到开发的目的。
早就听说过命令行大佬写代码从不用图形界面,直接ssh连过去就写。然鹅vim等编辑器不经过费劲的折腾实在是比不上现代ide方便,同时学习成本也很高,感谢微软黑科技,vscode的remote development 现阶段已经完善到一定程度,通过ssh连接linux服务器,就可以获得一个类似与linux下打开的vscode,同时随着vscode插件的完善,现在直接用vscode已经可以很快乐的写c/c++ python nodejs,java也已经可以相当愉快的写了。
以docker为代表的容器技术极大的方便了微服务下环境的搭建,只要是linux下一套环境,容器就可以将之打包,然后在任何装有docker下的机器上运行,类似于搬上去一整个虚拟机的运行环境。然鹅又没有虚拟机那么重量化,其实docker只是为你的运行组件隔离出来了一个环境,其底层还是用本身操作系统,这也就是为啥docker不能虚拟出一个windows环境一样,应为虚拟出来的环境本质还是用同一套内核的。
买不起云服务器的同学可以使用虚拟机替代,出了占用本机资源这一个缺点,虚拟机相比于自己花那一点小钱买的云服务器拥有更高的性能,同时和本机的连接不会受外部网路影响,同时你将会学到很多网络的知识
从前往后比就行了
二分,往上赶,上升那边一定有个峰值,然后选右值,每次也和+1的比
class Solution {
public int findPeakElement(int[] nums) {
int l = 0, r = nums.length - 1;
while( l < r)
{
int mid = ( l + r )/2;
if(nums[mid] > nums[mid + 1]) r = mid;
else l = mid + 1;
}
return r;
}
}
动态规划,比较传统dp[i][j] = dp[i-1][j]+dp[i][j-1];
需要o(n)解法,本质就是去重先放哈希set里,然后从前往后遍历,对每个都链起来,可以从小到大找,
用个虚假头指针
比 next 和next.next
这题得用栈,
深度优先搜索 mark数组 可以用过置为0
隔一个或者隔两个最大的 动态规划 一维dp数组即可
一比较简单,直接记录每一位前面最小价格,
二也比较简单,直接记录上升,忽略下降
三和四是最多两笔和最多k比,就要多个dp数组了,
两个dp数组,一个存最大,一个存最小,因为负负得正
二分,确定哪边有序后按范围筛
先排序,按啥排序呢,就是a+b大还是b+a大,拼接,然后大的放前面,拼起来 怎么拼就是比如999和88先让999*100加88
双指针,一个保证有效
b+数结构 ,指针
String resource = "config.xml";InputStream inputStream = Resources.getResourceAsStream(resource);
mybatis 的 getResourceAsStream(resource) 方法主要通过
ClassLoader[] getClassLoaders(ClassLoader classLoader) { return new ClassLoader[]{ classLoader, defaultClassLoader, Thread.currentThread().getContextClassLoader(), getClass().getClassLoader(), systemClassLoader};}
获得classloader 然后调用 getResourceAsStream(resource) 方法来获取classpath 下文件的InputStream
看完基本用法后发现对生命周期这一块理解薄弱在此欲小小翻译一下官方文档中GettingStarted中关于生命周期的说法
Scope and Lifecycle
It’s very important to understand the various scopes and lifecycles classes we’ve discussed so far. Using them incorrectly can cause severe concurrency problems.
NOTE Object lifecycle and Dependency Injection Frameworks
Dependency Injection frameworks can create thread safe, transactional SqlSessions and mappers and inject them directly into your beans so you can just forget about their lifecycle. You may want to have a look at MyBatis-Spring or MyBatis-Guice sub-projects to know more about using MyBatis with DI frameworks.
SqlSessionFactoryBuilder
This class can be instantiated, used and thrown away. There is no need to keep it around once you’ve created your SqlSessionFactory. Therefore the best scope for instances of SqlSessionFactoryBuilder is method scope (i.e. a local method variable). You can reuse the SqlSessionFactoryBuilder to build multiple SqlSessionFactory instances, but it’s still best not to keep it around to ensure that all of the XML parsing resources are freed up for more important things.
SqlSessionFactory
Once created, the SqlSessionFactory should exist for the duration of your application execution. There should be little or no reason to ever dispose of it or recreate it. It’s a best practice to not rebuild the SqlSessionFactory multiple times in an application run. Doing so should be considered a “bad smell”. Therefore the best scope of SqlSessionFactory is application scope. This can be achieved a number of ways. The simplest is to use a Singleton pattern or Static Singleton pattern.
SqlSession
Each thread should have its own instance of SqlSession. Instances of SqlSession are not to be shared and are not thread safe. Therefore the best scope is request or method scope. Never keep references to a SqlSession instance in a static field or even an instance field of a class. Never keep references to a SqlSession in any sort of managed scope, such as HttpSession of the Servlet framework. If you’re using a web framework of any sort, consider the SqlSession to follow a similar scope to that of an HTTP request. In other words, upon receiving an HTTP request, you can open a SqlSession, then upon returning the response, you can close it. Closing the session is very important. You should always ensure that it’s closed within a finally block. The following is the standard pattern for ensuring that SqlSessions are closed:
try (SqlSession session = sqlSessionFactory.openSession()) { // do work}Using this pattern consistently throughout your code will ensure that all database resources are properly closed.
Mapper Instances
Mappers are interfaces that you create to bind to your mapped statements. Instances of the mapper interfaces are acquired from the SqlSession. As such, technically the broadest scope of any mapper instance is the same as the SqlSession from which they were requested. However, the best scope for mapper instances is method scope. That is, they should be requested within the method that they are used, and then be discarded. They do not need to be closed explicitly. While it’s not a problem to keep them around throughout a request, similar to the SqlSession, you might find that managing too many resources at this level will quickly get out of hand. Keep it simple, keep Mappers in the method scope. The following example demonstrates this practice.
try (SqlSession session = sqlSessionFactory.openSession()) { BlogMapper mapper = session.getMapper(BlogMapper.class); // do work}
理解那些我么将要讨论的多种多样的域和生命周期级别是非常重要的,不正确地使用他们会导致多种多样的并发问题
NOTE Object lifecycle and Dependency Injection Frameworks
DI框架可以创建线程安全,事务型的SqlSessions 和 mappers 然后直接将它们注入到beans中,所以你可以不用考虑他们的生命周期.你可能需要看一下MyBatis-Spring和MyBatis-Guice这两个子项目去了解如何配合DI框架使用MyBatis
这个类可以被实例化,使用和抛出,在你创造了你的SqlSessionFactory后它就没必要继续被保持了,因此SqlSessionFactoryBuilder实例的最佳的域是方法域(比如作为一个本地方法的变量),你依然可以保留SqlSessionFactoryBuilder用以创造多个的SqlSessionFactory实例,但是为了所有和XML语法分析的资源得以释放去做更重要的事,我们最好还是不要保留他.
一旦被创建,SqlSessionFactory就应该在你整个应用执行期间存在,我们甚至几乎都没有理由去销毁或者重新创造它,在一个应用运行期间不重新构建SqlSessionFactory多次是一个最佳的惯例,而不这样做通常会被人们认为是个坏事情.因此最好的SqlSessionFactory域应该是application域,有很多种方式可以做到这样,最简单的就是用一个单例模式或者静态单例模式。
每个线程应该拥有它自己的SqlSession实例,SqlSession的实例是不被线程共享的,也是线程不安全的,因此对它来说最好的域是方法域和请求域,永远不要将一个SqlSession实例的引用放在一个静态字段甚至一个类的实例字段中,也永远不要放在任何形式的被管理的域比如Servlet framework的HttpSession中,如果你用了任何形式的web框架,考虑把SqlSession放在一个和HTTP 请求差不多的域中,用另一句话来说就是当收到一个HTTP请求,你就可以开一个SqlSession,然后当返回这个相应的时候,你可以关闭它,关闭这个回话是非常重要的,你必须永远确保会话被关闭在一个finally块,下面是确保会话被关闭的标准模式写法:
try (SqlSession session = sqlSessionFactory.openSession()) { // do work}
While it’s not a problem to keep them around throughout a request, similar to the SqlSession, you might find that managing too many resources at this level will quickly get out of hand. Keep it simple, keep Mappers in the method scope. The following example demonstrates this practice.
Mappers是那些你创建来用来绑定你的数据库映射语句的接口,Mapper接口的实例是从SqlSession中获得的。如此来说,从技术上讲Mappers域的最大边界和SqlSession的域(requested域)是一样的。然而,对于mapper的实例来说最好的域是方法域,也就是说它必须在使用它的方法中被请求然后用完后被丢弃,他们不需要被明确地关闭。当然和SqlSession一样保留它们在一整个请求也是没问题的,但是你可能会发现在这种层面上管理太多资源将会马上失控,下面的例子证明了这个实践:
try (SqlSession session = sqlSessionFactory.openSession()) { BlogMapper mapper = session.getMapper(BlogMapper.class); // do work}
实训作业,数据库的管理系统,应付老师使用mybtais+springboot+vue+element ui 搭建了一个小区的物业管理系统,复习了一下这些技术栈,不过js确实把我迷的啊~~
后端就是简单的curd 前端是单页面应用,没有保持登录的功能,主要是token技术还没熟练,所以这次主要是ui上的工作
之前学vue的时候功力尚浅,只知然不知其所以然,这次有了新的理解,
.
├── README.md
├── babel.config.js
├── node_modules/
├── package-lock.json
├── package.json
├── public/
├── src/
└── vue.config.js
笔者没有细致的学过node npm 对于其中的理解不求精准专业,但求易懂,望网友包容
node的出现使得js跳出浏览器的范围,js可以变身类似与python这样的语言操作文件,监听网络等等,vuecli就是类似于这样的一个“软件”,npm 安装包的方式很是粗暴 全局的存在用户目录下,项目的直接存在项目下(所以gitignore一定要把node_modules排除)
一个js文件,是自己新加的,vuecli应该认识,可以读取里面的设置
代码
favicon和一个基础的html内容提示:
We're sorry but wuliu doesn't work properly without JavaScript enabled. Please enable it to continue
还有个id=app的div
package.json 这个文件是 npm init 时创建的一个文件,会记录当前整个项目中的一些基础信息。而 package-lock.json 这个文件却是 node_modules 文件夹或者 package.json 文件发生变化时自动生成的。这个文件主要功能是确定当前安装的包的依赖,以便后续重新安装的时候生成相同的依赖,而忽略项目开发过程中有些依赖已经发生的更新。
安装项目域的node模块
babel官网正中间一行黄色大字写着“babel is a javascript compiler”,翻译一下就是babel是一个javascript转译器。为什么会有babel存在呢?原因是javascript在不断的发展,但是浏览器的发展速度跟不上。以es6为例,es6中为javascript增加了箭头函数、块级作用域等新的语法和Symbol、Promise等新的数据类型,但是这些语法和数据类型并不能够马上被现在的浏览器全部支持,为了能在现有的浏览器上使用js新的语法和新的数据类型,就需要使用一个转译器,将javascript中新增的特性转为现代浏览器能理解的形式。babel就是做这个方面的转化工作。
.
├── App.vue
├── assets
│ └── logo.png
├── components
│ ├── Complaint.vue
│ ├── Express.vue
│ ├── NeedPay.vue
│ ├── NeedRepair.vue
│ ├── NewRepair.vue
│ ├── Paid.vue
│ ├── ShowInfo.vue
│ ├── ShowProperty.vue
│ ├── ShowUser.vue
│ ├── UploadInfo.vue
│ ├── UploadProperty.vue
│ └── admin
├── main.js
├── router
│ └── index.js
└── views
├── Admin.vue
├── Login.vue
├── Main.vue
├── Register.vue
└── ULogin.vue
任何.vue文件分三个部分:
<template>
</template>
<script>
export default {
}
</script>
<style>
</style>
这里主要是script部分 export是 node部分的语法参考
将模块暴露出去
查看Admin.vue是如何引用其他模块的:
<Express/>
import Express from '../components/admin/Express'
export default {
name: 'Admin',
components: {
Express
}
}
说几个要点:
ui框架使得我的开发变得很容易,现在基本上回不去了,是单体开发的重要快捷方法,文档易懂
var _this = this
let param = new URLSearchParams()
param.append('name', this.form.username)
param.append('pwd', this.form.password)
this.$axios.post('login', param)
.then(function (response) {
console.info(response.data)
if (response.data.type === 0) {
_this.$router.push({ name: 'Main', params: { user: response.data } })
} else {
_this.$alert('请使用管理员平台登录', '提示', {})
}
})
this的指向的,js的大坑,除了上面的方法,还可使用箭头函数,不过js的报错还是不够友好难找到错误,学好js多用多看chrome的控制台,
params 是参数,是请求头的部分,springmvc用@RequestParam 接收
其他的js对象在提交的时候则是放在body部分以json形式传输在springmvc中可以用@requestbody接收
使用postman生成代码如下
POST /express/get/4 HTTP/1.1
Host: localhost:8081
Content-Type: application/x-www-form-urlencoded
username: abc
Cache-Control: no-cache
Postman-Token: 227f3304-2477-4cb6-ae6f-dee9931d11f1
{
"owner_id": 4,
"create_time": "2019-12-17T16:00:00.000Z"
}
空行后是body部分
而param在前面
Log4j有7种不同的log级别,按照等级从低到高依次为:TRACE、DEBUG、INFO、WARN、ERROR、FATAL、OFF。如果配置为OFF级别,表示关闭log .
log4j也很久没有更新了,现在已经有很多其他的日志框架对Log4j进行了改良,比如说SLF4J、Logback等
springboot 默认实现
一个统一的接口
SLF4J,即简单日志门面(Simple Logging Facade for Java),不是具体的日志解决方案,而是通过Facade Pattern提供一些Java logging API,它只服务于各种各样的日志系统。按照官方的说法,SLF4J是一个用于日志系统的简单Facade,允许最终用户在部署其应用时使用其所希望的日志系统。作者创建SLF4J的目的是为了替代Jakarta Commons-Logging。
实际上,SLF4J所提供的核心API是一些接口以及一个LoggerFactory的工厂类。在使用SLF4J的时候,不需要在代码中或配置文件中指定你打算使用那个具体的日志系统。类似于Apache Common-Logging,SLF4J是对不同日志框架提供的一个门面封装,可以在部署的时候不修改任何配置即可接入一种日志实现方案。但是,他在编译时静态绑定真正的Log库。使用SLF4J时,如果你需要使用某一种日志实现,那么你必须选择正确的SLF4J的jar包的集合(各种桥接包)。SLF4J提供了统一的记录日志的接口,只要按照其提供的方法记录即可,最终日志的格式、记录级别、输出方式等通过具体日志系统的配置来实现,因此可以在应用中灵活切换日志系统。
先决条件:
装好wsl 无论 1和2 参考微软官方教程或自行百度
PS: 微软官方文档质量超高
以斜杠开头 表示 文件夹嵌套关系的一个路径
windows下一般这样:C:\Users\h-l-j
linux : /home/
sudo vim /etc/apt/sources.list
进入vim后 按下冒号(就是打出一个":",英语输入法)
回车
按下冒号: 输入 x 回车
更新软件源 sudo apt update
linux 的另一个好处是安装啥语言都一个命令就安装完了(以下命令只适用ubuntu,其他linux请自行查找)
ctrl + `(tab上方) 打开命令行/shell
插件 打开各种语言都会推荐你,可以自行探索
snippet 可以装逼
首先打开vscode 装 wsl 插件
然后linux 新建一个ccc 目录
用 code ccc的命令打开这个目录
新建 .c 或者 .cpp结尾的文件,
按照提示安装c的插件(网络原因卡了可重开)
ctrl + `(tab上方) 打开命令行/shell
近期学习计算机网路,按目前认知先大概地分一下层
首先以网卡来一分为二,上面的都是软件实现的,网卡根据mac地址发送给下一个网卡
讨论一下 网卡以上的,参考《网络是如何连接的》 自认为用模块 这个概念比较合适,
毕竟以我看来,这些都是一个个软件(层),对数据进行封装(整理,添加某些的东西)
这里我们举个例子:
网页本身是 按html规范的字符串,在http这个层面 就加了 啥啥啥
下列这种字符串 后面在内容的里面还是原来的html
Request URL: https://www.baidu.com/Request Method: GETStatus Code: 200 OKRemote Address: 127.0.0.1:1080Referrer Policy: no-referrer-when-downgrade
后来 tcp 又要 加点东西了
就是传说中 的 tcp包 这时候 就会把 http 上封装好的东西看作一个整体,当让也可以发自定义的字符串(字节流)
后来 ip 部分 又要加点东西,,,类似上面的
加完这些东西,把这个数据 丢给网卡,网卡按照上面的mac地址发个和它相连的网卡
90年代,以太网取得垄断地位,以太网成为局域网代名词。
数据链路层
仅需要MAC层,采用以太网帧格式(Ethernet V2封装:ARPA).
最底层的电路层,具体谈论传输介质啊,方式啥的,比如无线电波,光缆啥的
主要就是数字信号的发送
PS: 数字信号和模拟信号:
现阶段,由于传输会失真等原因,数字信号采用只有高低两个点频的二进制数字信号,而模拟信号,主要在耳机孔、vga视频线上,这里拿耳机线举例,直接用电流/电压大小形成的波模拟声波,耳机直接按电流变化改变振动频率即可
在用redis 缓存查询结果 更新的时候,在并发访问下,可以设置各延时存储,两秒。
普通索引查找过程第二步
回表查询
先通过普通索引的值定位聚簇索引值,再通过聚簇索引的值定位行记录数据,需要扫描两次索引B+树,它的性能较扫一遍索引树更低。
索引覆盖
只需要在一棵索引树上就能获取SQL所需的所有列数据,无需回表,速度更快。例如:select id,age from user where age = 10;
如何实现覆盖索引
常见的方法是:将被查询的字段,建立到联合索引里去。1、如实现:select id,age from user where age = 10;
explain分析:因为age是普通索引,使用到了age索引,通过一次扫描B+树即可查询到相应的结果,这样就实现了覆盖索引
jvm相信大家都了解,一个jvm进程对应一个操作系统进程,而在hotspot虚拟机里一个java线程也和操作系统的线程一一对应
当线程本地存储、缓
冲区分配、同步对象、栈、程序计数器等准备好以后,就会创建一个操作系统原生线程。
Java 线程结束,原生线程随之被回收。操作系统负责调度所有线程,并把它们分配到任何可
用的 CPU 上。当原生线程初始化完毕,就会调用 Java 线程的 run() 方法。当线程结束时,
13/04/2018 Page 21 of 283
会释放原生线程和 Java 线程的所有资源。
虚拟机线程 (VM thread) 这个线程等待 JVM 到达安全点操作出现。这些操作必须要在独立的线程里执行,因为当 堆修改无法进行时,线程都需要 JVM 位于安全点。这些操作的类型有:stop-theworld 垃圾回收、线程栈 dump、线程暂停、线程偏向锁(biased locking)解除。
周期性任务线程 这线程负责定时器事件(也就是中断),用来调度周期性操作的执行。
GC 线程 这些线程支持 JVM 中不同的垃圾回收活动。
编译器线程 这些线程在运行时将字节码动态编译成本地平台相关的机器码。
信号分发线程 这个线程接收发送到 JVM 的信号并调用适当的 JVM 方法处理。
代理设计模式:面向对象中常见的设计模式,核心**就是将核心功能转交给代理者完成,完成核心功能和非核心功能的解耦。
动态代理和静态代理:静态代理,怎么代理的代码写死,要实现一个新的代理功能就要新写一个一段完整的代码;动态代理:运用反射动态地在内存或者磁盘中创建类并运行。
AOP:面向切面编程,看一下维基百科上的解释:
In computing, aspect-oriented programming (AOP) is a programming paradigm that aims to increase modularity by allowing the separation of cross-cutting concerns.
AOP是一种编程范式用来增加程序的模块化通过横切关联点的分类(不是人话)。解释一下:传统的业务逻辑是从头到尾一块一块执行的,将整块的逻辑分类出核心和非核心(横切)。
在这个框架的AOP实现中,再扫描包的时候将所有的AOP信息(切点,通知)注册:
@Override
public Map<String, List<AspectAdvisor>> resolve() throws ResolvedException {
Map<String, List<AspectAdvisor>> beanAdvisorMapper = new HashMap<>();
for (BeanDefinition beanDefinition : beanDefinitions) {
if (beanDefinition.getClazz().getAnnotation(Aspect.class) != null) {
List<AspectAdvisor> advisors = resolveAdvisors(beanDefinition);
beanAdvisorMapper.put(beanDefinition.getBeanName(), advisors);
}
}
return beanAdvisorMapper;
}
然后获得对象的时候生产代理对象将切面“织入”进来,完成对方法的增强:
一下是cjlb产生代理类的过程,其中this本身实现了MethodInterceptor接口
@Override
public Object newProxyInstance() throws BeansException {
Enhancer enhancer = new Enhancer();
enhancer.setCallback(this);
Class targetClass = target.getClass();
Object[] args = null;
//处理多实例类
if (targetClass == UnSharedInstance.class) {
UnSharedInstance unSharedInstance = (UnSharedInstance) target;
targetClass = unSharedInstance.getClazz();
args = unSharedInstance.getArgs();
}
enhancer.setSuperclass(targetClass);
//执行无参构造方法
if (args == null) {
return enhancer.create();
}
//获取参数
Class[] argumentTypes = Arrays.stream(args).map(Object::getClass).toArray(Class[]::new);
return enhancer.create(argumentTypes, args);
}
下面是重写的intercept方法:
@Override
public Object intercept(Object obj, Method method, Object[] args, MethodProxy proxy) throws Throwable {
//获取真实目标实例
Object targetObject = getTargetObject();
Object result = invokeObjectMethod(targetObject, method, args);
if (result == null) {
result = advisorInvocationHandler.invokeWithAdvice(targetObject, method, args);
}
return result;
}
对方法执行时期debug可以看动intercept方法是如何起作用的,AOP主要通过advisorInvocationHandler.invokeWithAdvice(targetObject, method, args)这个方法实现:
public Object invokeWithAdvice(Object target, Method method, Object[] args) throws Throwable {
MethodSignature methodSignature = new MethodSignature(target.getClass(), method);
MethodBeforeAdvice[] beforeAdvices = getMethodBeforeAdvices(method, methodSignature);
MethodInvocation invocation = new MethodInvocation(target, method, args, beforeAdvices);
MethodInterceptor[] aroundAdvices = getAroundAdvices(method, methodSignature);
if (aroundAdvices.length > 0) {
//执行环绕通知
for (MethodInterceptor aroundAdvice : aroundAdvices) {
try {
Object returnValue = doAround(aroundAdvice, invocation);
doAfterReturning(invocation, returnValue);
return returnValue;
} catch (Exception e) {
doThrows(invocation, e);
}
}
} else {
try {
Object returnValue = invocation.proceed();
doAfterReturning(invocation, returnValue);
return returnValue;
} catch (Exception e) {
doThrows(invocation, e);
}
}
return null;
}
具体是怎么获得通知是在getProxyBean()的时候
private AdvisorInvocationHandler getAdviceInvocationHandler() {
AdvisorInvocationHandler advisorInvocationHandler = new AdvisorInvocationHandlerImpl();
if (advisors.size() > 0) {
advisorInvocationHandler.setAdvisors(advisors);
}
if (beanFactory != null) {
//从BeanFactory获取Aspect通知
AdvisorBeanFactoryImpl advisorBeanFactoryImpl = (AdvisorBeanFactoryImpl) beanFactory;
advisorInvocationHandler.setAdvisors(advisorBeanFactoryImpl.getAdvisors());
}
return advisorInvocationHandler;
}
动态代理在增加灵活性方面有巨大作用除了aop在很多框架中存在,spring-toy的ioc部分,以及mybatis 等著名框架,为了“非侵入性”原则,都会使用动态代理。理解动态代理,在很多使用框架的时候就可以了解其内部原理。
归并排序,先用快慢指针找到中点后断开,对两边分别排序,再merge一下两个结果
就是找字典序的下一个排列,如果是完全递减的,就直接反转成递增的,然后 为了影响范围足够小,从后往前找到第一个递增的。然后把这个递增的前一个数字替换掉,具体就是后面从后往前遍历找一个比这个数字大的和这个互换,然后对后面的做升序排序,只要反转即可。
直接dum指针是假头,然后 cur cur.next cur.next.next 直接进行删
动态规划,子序列不是子串,可以不连续,然后为了防止i-1小于下标,所以dp[][]每个维度长度都长一位,如果i j的字母相同 dp[i][j] = dp[i-1][j-1],如果不相同,就是比较 dp[i][j-1] 和 dp[i-1][j] 哪个大,
二分查找 比较在哪两个整数中间
直接相加
本身没啥难的,但是很复杂
这题有个核心**是从前往后遍历。当前位置选不选都有两种可能,然后分别递归到下一位
用个辅助栈存当前位置最小的
class MinStack {
Deque<Integer> xStack;
Deque<Integer> minStack;
public MinStack() {
xStack = new LinkedList<Integer>();
minStack = new LinkedList<Integer>();
minStack.push(Integer.MAX_VALUE);
}
public void push(int x) {
xStack.push(x);
minStack.push(Math.min(minStack.peek(), x));
}
public void pop() {
xStack.pop();
minStack.pop();
}
public int top() {
return xStack.peek();
}
public int getMin() {
return minStack.peek();
}
}
字符串分割后存下,不然就从前往后遍历,到. 每段比
这题写起来贼复杂
核心dfs的时候
// 如果还没有找到 4 段 IP 地址就已经遍历完了字符串,那么提前回溯
if (segStart == s.length()) {
return;
}
// 由于不能有前导零,如果当前数字为 0,那么这一段 IP 地址只能为 0
if (s.charAt(segStart) == '0') {
segments[segId] = 0;
dfs(s, segId + 1, segStart + 1);
}
// 一般情况,枚举每一种可能性并递归
int address = 0;
for (int segEnd = segStart; segEnd < s.length(); ++segEnd) {
address = address * 10 + s.charAt(segEnd) - '0';
if (address > 0 && address <= 255) { //这么递归
segments[segId] = address;
dfs(s, segId + 1, segEnd + 1);
} else {
break;
}
}
}
经典题
class Solution {
private Map<Integer, Integer> indexMap;
public TreeNode myBuildTree(int[] preorder, int[] inorder, int preorder_left, int preorder_right, int inorder_left, int inorder_right) {
if (preorder_left > preorder_right) {
return null;
}
// 前序遍历中的第一个节点就是根节点
int preorder_root = preorder_left;
// 在中序遍历中定位根节点
int inorder_root = indexMap.get(preorder[preorder_root]);
// 先把根节点建立出来
TreeNode root = new TreeNode(preorder[preorder_root]);
// 得到左子树中的节点数目
int size_left_subtree = inorder_root - inorder_left;
// 递归地构造左子树,并连接到根节点
// 先序遍历中「从 左边界+1 开始的 size_left_subtree」个元素就对应了中序遍历中「从 左边界 开始到 根节点定位-1」的元素
root.left = myBuildTree(preorder, inorder, preorder_left + 1, preorder_left + size_left_subtree, inorder_left, inorder_root - 1);
// 递归地构造右子树,并连接到根节点
// 先序遍历中「从 左边界+1+左子树节点数目 开始到 右边界」的元素就对应了中序遍历中「从 根节点定位+1 到 右边界」的元素
root.right = myBuildTree(preorder, inorder, preorder_left + size_left_subtree + 1, preorder_right, inorder_root + 1, inorder_right);
return root;
}
public TreeNode buildTree(int[] preorder, int[] inorder) {
int n = preorder.length;
// 构造哈希映射,帮助我们快速定位根节点
indexMap = new HashMap<Integer, Integer>();
for (int i = 0; i < n; i++) {
indexMap.put(inorder[i], i);
}
return myBuildTree(preorder, inorder, 0, n - 1, 0, n - 1);
}
}
其中核心是,找到中序遍历那个root节点后,可以算出左子树节点个数,就是左边的数量,然后前序遍历数组的前n个就是左子树的,后几个就是右子树的
class Solution {
public int coinChange(int[] coins, int amount) {
int[] dp = new int[amount+1];
dp[0] = 0;
for(int i = 1; i < amount+1; i++) {
dp[i] = 99999;
for(int j = 0; j < coins.length; j++) {
if(coins[j] <= i) {
dp[i] = Math.min(dp[i], dp[i-coins[j]]+1);
}
}
}
return dp[amount] > amount? -1:dp[amount];
}
}
这题非常巧妙,虽然两层for循环,dp的含义为。以 coin[j] 为结尾的最小数量, 重点记住这段 Math.min(dp[i], dp[i-coins[j]]+1); 然后每个 金额,为外层循环,以啥为结尾内层循环,然后 比如以5为结尾的结果会受到以 2为结尾的影响
滑动窗口,两个指针如何判断区间包含呢。用一个哈希表记录每个元素出现的次数。和另一个哈希表记录目标字符串的各个元素数量比较,代码毕竟复杂
这题首先把所有负数都改成+999999最大的正数。难点在于不利用额外空间。然后,可以利用数组本身作为哈希表,但是又要求不要用额外空间,之前把所有负数都变成正数就是为了用正负来打标签,从前往后遍历,数组第i位有就变负数,等于用负数来作为boolean
单调队列
队列记录下标
本质就是维护一个递减的队列,为了首位超出范围后能直接使用后一位,所以每次都从后往前遍历,把比自己小的全去掉。第二步就是判断首位在不在范围内,不在也去掉,第三步取首位
记住几个linkedlist 的方法,addFirst peek poll addLast peekLast
Spring Cloud为开发人员提供了工具来快速构建分布式系统中一些常见的模式(例如配置管理、服务发现、断路器、智能路由、micro-proxy、 control bus控制总线、one-time tokens 、global locks 全局锁、 leadership election ,分布式会话、集群状态)。组织分布式系统使之模板化(Coordination of distributed systems leads to boiler plate patterns ),使用Spring Cloud开发者可以快速建立实现这些模式的服务和应用程序。他们将适用于任何分布式环境中,包括开发人员自己的笔记本电脑,物理机( bare metal data centers)数据中心,云计算等和管理平台。
Spring Cloud focuses on providing good out of box experience for typical use cases and extensibility mechanism to cover others.
统一化的管理,可以实现各个微服务间良好的解耦,服务提供者和消费者都注册到Eureka服务器上,浏览器访问消费者地址,消费者会通过Eureka服务器上服务提供者的名字,找到提供者的真实路径,进行调用。高可用时可以搭建几个Eureka服务器,互相注册,复制各自信息。
贴一下关键代码:
项目开始前更改host 将 discovery 映射到127.0.0.1
m1\src\main\java\xyz\lennon\EurekaApplication.java
package xyz.lennon;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.netflix.eureka.server.EnableEurekaServer;
@SpringBootApplication
@EnableEurekaServer
public class EurekaApplication {
public static void main(String[] args) {
SpringApplication.run(EurekaApplication.class, args);
}
}
application.yml
server:
port: 8761 # 指定该Eureka实例的端口
eureka:
instance:
hostname: discovery # 指定该Eureka实例的主机名
client:
registerWithEureka: false
fetchRegistry: false
serviceUrl:
defaultZone: http://${eureka.instance.hostname}:${server.port}/eureka/
m2\src\main\java\xyz\lennon\UserApplication.java
package xyz.lennon;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
@SpringBootApplication
@EnableDiscoveryClient
public class UserApplication {
public static void main(String[] args) {
SpringApplication.run(UserApplication.class, args);
}
}
application.yml
server:
port: 8001
spring:
application:
name: microservice-provider-user # 项目名称尽量用小写
jpa:
generate-ddl: false
show-sql: true
hibernate:
ddl-auto: none
datasource: # 指定数据源
platform: h2 # 指定数据源类型
schema: classpath:schema.sql # 指定h2数据库的建表脚本
data: classpath:data.sql # 指定h2数据库的insert脚本
logging:
level:
root: INFO
org.hibernate: INFO
org.hibernate.type.descriptor.sql.BasicBinder: TRACE
org.hibernate.type.descriptor.sql.BasicExtractor: TRACE
com.itmuch.youran.persistence: ERROR
eureka:
client:
serviceUrl:
defaultZone: http://discovery:8761/eureka/ # 指定注册中心的地址
instance:
preferIpAddress: true
启动上述两个模块,打开 http://discovery:8761/ 就可以看到监控页面注册的用户模块,用户模块也可以同时启动在不同端口上,注册到eureka,值得注意的是,应用名字如果相同,注册中心就会认为是同一个服务的不同实例。
借助Ribbon实现负载均衡, 规则用默认的。
开启eureka server 和用户模块,为了模拟负载均衡,在不同的端口启动多个用户模块
m3\src\main\java\xyz\lennon\MovieRibbonApplication.java
package xyz.lennon;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.context.annotation.Bean;
import org.springframework.web.client.RestTemplate;
@SpringBootApplication
@EnableDiscoveryClient
public class MovieRibbonApplication {
/**
* 实例化RestTemplate,通过@LoadBalanced注解开启均衡负载能力.
* @return restTemplate
*/
@Bean
@LoadBalanced
public RestTemplate restTemplate() {
return new RestTemplate();
}
public static void main(String[] args) {
SpringApplication.run(MovieRibbonApplication.class, args);
}
}
m3\src\main\java\xyz\lennon\RibbonService.java
package xyz.lennon;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;
/**
* dfd
*/
@Service
public class RibbonService {
@Autowired
private RestTemplate restTemplate;
public User findById(Long id) {
return this.restTemplate.getForObject("http://microservice-provider-user/" + id, User.class);
}
}
可以看到restTemplate是基于注册的应用名调用的
server:
port: 8010
spring:
application:
name: microservice-consumer-movie-ribbon
eureka:
client:
serviceUrl:
defaultZone: http://discovery:8761/eureka/
instance:
preferIpAddress: true
多次请求 http://localhost:8010/ribbon/1 观察后台日志发现负载均衡起作用了
Hystrix 防止在服务之间调用失败时“卡死”的情况,停止无意义的调用并给出监控信息
对比上文模块的改进:
m4\src\main\java\xyz\lennon\MovieRibbonHystrixApplication.java
package xyz.lennon;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.cloud.client.circuitbreaker.EnableCircuitBreaker;
import org.springframework.cloud.client.discovery.EnableDiscoveryClient;
import org.springframework.cloud.client.loadbalancer.LoadBalanced;
import org.springframework.context.annotation.Bean;
import org.springframework.web.client.RestTemplate;
/**
* 使用@EnableCircuitBreaker注解开启断路器功能
*
* @author eacdy
*/
@SpringBootApplication
@EnableDiscoveryClient
@EnableCircuitBreaker
public class MovieRibbonHystrixApplication {
/**
* 实例化RestTemplate,通过@LoadBalanced注解开启均衡负载能力.
* @return restTemplate
*/
@Bean
@LoadBalanced
public RestTemplate restTemplate() {
return new RestTemplate();
}
public static void main(String[] args) {
SpringApplication.run(MovieRibbonHystrixApplication.class, args);
}
}
m3\src\main\java\xyz\lennon\RibbonHystrixService.java
package xyz.lennon;
import com.netflix.hystrix.contrib.javanica.annotation.HystrixCommand;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.web.client.RestTemplate;
@Service
public class RibbonHystrixService {
@Autowired
private RestTemplate restTemplate;
private static final Logger LOGGER = LoggerFactory.getLogger(RibbonHystrixService.class);
/**
* 使用@HystrixCommand注解指定当该方法发生异常时调用的方法
* @param id id
* @return 通过id查询到的用户
*/
@HystrixCommand(fallbackMethod = "fallback")
public User findById(Long id) {
return this.restTemplate.getForObject("http://microservice-provider-user/" + id, User.class);
}
/**
* hystrix fallback方法
* @param id id
* @return 默认的用户
*/
public User fallback(Long id) {
RibbonHystrixService.LOGGER.info("异常发生,进入fallback方法,接收的参数:id = {}", id);
User user = new User();
user.setId(-1L);
user.setUsername("default username");
user.setAge(0);
return user;
}
}
可以看到restTemplate是基于注册的应用名调用的
server:
port: 8011
spring:
application:
name: microservice-consumer-movie-ribbon-with-hystrix
eureka:
client:
serviceUrl:
defaultZone: http://discovery:8761/eureka/
instance:
hostname: ribbon # 此处,preferIpAddress不设置或者设为false,不能设为true,否则影响turbine的测试。turbine存在的问题:eureka.instance.hostname一致时只能检测到一个节点,会造成turbine数据不完整
关闭用户模块,并没有无响应,而是 获得结果:{"id":-1,"username":"default username","age":0}
,另外日志打印:c.i.c.s.u.service.RibbonHystrixService : 异常发生,进入fallback方法,接收的参数:id = 1
。
计算机发展这么多年,流行的语言在其各自擅长的领域都是不可替代的存在;同时在前几门语言中,我们也没有看到两个定位相似的语言。排名前几的语言没有高下之分。
常年排名第一,岗位也是最多的,虽然理论上适合干几乎所有领域,但现在其实也就在服务器端称王了,其他领域还是弟弟,其严格的类型约束,良好的面向对象支持,GC机制和不错的运行速度很适合大型超大型的项目开发,再加上在服务器领域深耕多年,生态完整,所以很适合干后端。(其实gui开发也是他擅长的,另一门和java很相似的语言c#就很适合开发windows桌面应用,安卓也是java开发,java自带的gui框架swingjavaFX在上个时代还是很流行的,谷歌新的gui框架flutter用的也是一门和java类似的语言dart,但总结,java的gui要么被类似的替代了,要么过时了)
两个特点
c太过底层,不支持面向对象,主要就是在嵌入式那边开发底层驱动,或者写个操作系统内核(linux内核就是c写的,据说如果用c++重写可以减少四分之三的代码量,但是Linus就是不这样,怕c++有坑)
c++不只是c的面向对象版本,事实上他太大而全了,啥都能写,而且任何一个程序员都可以按照自己的喜好把c++写的像另一门语言(在我接触的Qt给我的感觉就是设计的很像java),但这是一门为“聪明人”设计的语言,而大多数人“不聪明”。所以我认为这在很多情况下不是门"好语言",以前的c++可谓个方面通吃,现在
就是网页了,B/S架构越来越流行,而浏览器里除了js几乎不可能出现第二种语言,并且这几年js有向外的发展趋势(app,后端),(js语法有很多坑,这几年的es5 6 7和typescript就在填坑)
毫无疑问这几门应该都要会,c/c++主要作用在于完成老师作业和刷算法题
python在爬数据,写一些小脚本方面很有用(主要是库多)
java 和 js 作为互联网中几乎占据**地位的语言工作岗位最多
双指针滑动窗口
自己搞一个+HashMap + 双端链表, 不要用linkedlist ,然后实现这个双端链表的更新一个数据到头部的方法(刷新最近使用),记录链表的节点数,超了就踢掉最后一个节点
类似快速排序 ,但是只要管k所在侧的排序
用循环第一次访问到第k个将第k个断开,然后用循环再反转这个k个。因为上次断开了
先排序 从小到大,然后定住第一个值,对后面的数字进行一左一右双指针往中间移。如果重复就跳过
动态规划, dp[i]的含义为包含第i位的最大值,从前往后遍历即可
三指针二分 判断mid指针是否大于nums[0] 确定哪边有序,然后判断数是否在这个nums[mid] 和 头 或尾范围内
中心点往外扩散,要注意中心的可能是个缝,回文子串是个双数
显然,如果我们真的在买卖股票,我们肯定会想:如果我是在历史最低点买的股票就好了!太好了,在题目中,我们只要用一个变量记录一个历史最低价格 minprice,我们就可以假设自己的股票是在那天买的。那么我们在第 i 天卖出股票能得到的利润就是 prices[i] - minprice。
mark[][]数组标记 两层for循环 深度优先搜索,直接向四周扩散
判断有环 双指针 快慢 相遇 找环起点 双指针 相遇后另一个指针从头开始,再等相遇
广度优先一样,用队列,一边从左到右,一下从右到左
如果是二叉搜索树,那可以安装搜索树的特性找到两个节点,然后通过记录父节点列表,找到父节点列表里的最近一样的
如果普通二叉树,那就是递归查找两个值,返回能否找到两个值中的一个,如果一个root左右两个分别都能找到,那肯定是一左一右,那就是最近公共的
动态规划,
if (s.charAt(i) == ')') {
if (s.charAt(i - 1) == '(') {
dp[i] = (i >= 2 ? dp[i - 2] : 0) + 2;
} else if (i - dp[i - 1] > 0 && s.charAt(i - dp[i - 1] - 1) == '(') {
dp[i] = dp[i - 1] + ((i - dp[i - 1]) >= 2 ? dp[i - dp[i - 1] - 2] : 0) + 2;
}
maxans = Math.max(maxans, dp[i]);
}
另一个办法是利用栈,每次碰到左括号就压进去,碰到右括号就弹一个出来,然后栈的底部每次存一个无效的位点
class Solution {
public int longestValidParentheses(String s) {
int maxans = 0;
Deque<Integer> stack = new LinkedList<Integer>();
stack.push(-1);
for (int i = 0; i < s.length(); i++) {
if (s.charAt(i) == '(') {
stack.push(i);
} else {
stack.pop();
if (stack.isEmpty()) {
stack.push(i);
} else {
maxans = Math.max(maxans, i - stack.peek());
}
}
}
return maxans;
}
}
简单
简单
简单
最简单的是深度优先遍历,本质是回溯,上层传给下层上层的和,到叶子节点了返回回去,然后再返回值全加起来
左加右加起来算直径 和 然后dfs返回的是左右最深的加一
简简单单,深度遍历,找个list存一下
中序遍历递归
private boolean inorder(TreeNode node) {
if(node == null) return true;
boolean l = inorder(node.left);
if(node.val <= pre) return false;
pre = node.val;
boolean r = inorder(node.right);
return l && r;
}
动态规划 注意两个第一行特殊考虑
注意不能两个rand7加起来 因为对于14 只有一种组合 对于5 就有 2+3或者3+2两种了
核心是既然是rand7那就是七机制,7进制一位不大于十那就两位,这样就是49以下的数了。然后保证40以下 ,再➗10
原地旋转,比较麻烦,核心分清楚奇数还是偶数,如果偶数好办,所以两个循环条件一个是n/2 一个是(n+1)/2
class Solution {
public void rotate(int[][] matrix) {
int n = matrix.length;
for (int i = 0; i < n / 2; ++i) {
for (int j = 0; j < (n + 1) / 2; ++j) {
int temp = matrix[i][j];
matrix[i][j] = matrix[n - j - 1][i];
matrix[n - j - 1][i] = matrix[n - i - 1][n - j - 1];
matrix[n - i - 1][n - j - 1] = matrix[j][n - i - 1];
matrix[j][n - i - 1] = temp;
}
}
}
}
还算简单。不用额外空间可以先快慢指针找到中点后后半段反转
这道比较简单,直接回溯 ,两种情况,选当前还是不选,然后传一个还剩多少额度
另一题比较复杂,还要考虑重复,所以要首先排序
简单就是统计,时间复杂度最低也要o(n)
有个不需要额外空间的算法,投票算法 消消乐杀敌,可以用个count
就普通的二分查找后向前向后扩展
动态规划,先初始化第一行第一列,然后选左和上和左上三个中最小的正方形
min(dp(i - 1, j), dp(i, j - 1), dp(i - 1, j - 1)) + 1;
动态规划,dp[][]含义为以各自下标为结尾的最长重复
这题和解码ip一样都是很麻烦的题
shell是操作系统中人机交互最重要的一个东西,翻译为壳,意思是包住操作系统内核的壳
你们windows不管是打开我的电脑还是任何一个文件夹跳出来的那个被称为文件资源管理器的东西就是windows的shell
我们现在用的操作系统大多都是有图形化的界面的,这个得感谢乔布斯和比尔的推广(微软公司和苹果公司)
早期的电脑都是没有图像界面只有命令行的(样子可参考打开cmd然后全屏)
对于用户来说,自然是图像化的比命令行好理解好操作
但是对于程序员来说,当然是命令行的程序好写(程序员省点力,用户就多花点力气,反过来。。。反过来)
你们现在应该还没人接触到gui的编写,你们的c语言也是跳出一个黑框框
linux 或者说 unix 是目前it界占据大半江山的操作系统
linux系统的学习可以说是程序员的一条必经之路
此文 为 《现代操作系统》 第一章 “引论”
的归纳总结,主要 分为两个部分:
- 硬件和软件
稍加补充:
- 小概念
- 我的小感悟(待补充)
控制器
复杂操作 小型计算机
设备本身
规范化的接口 STAT
驱动 内核态/用户态
cpu 中断 阻塞
科学上网,又称翻墙
主要是某些网站他不遵守**法律,所以我国政府把他禁了,当然变相的也保护了本地企业。其实我是支持**政府这个行为的,可以有效防止**舆论权掌握在海外反华力量手中,此处不做过多讨论。总之虽然这一政策给我们程序员带来了巨大的不便,但我个人还是支持国家的这一行为的。
本质就是在国外(也可能是**香港)有一台电脑,他代替你访问那些被禁掉的网站,然后把数据发给你。所以问题就在于要有这样一台在国外的机器给你提供服务。
曾经我也是个白嫖党,但是免费的不好找,找到了还不稳定,稳定了还一两天就不能用了,所以后来我也花钱了,找了个最便宜的一个月五块钱,再也不担心不费事了。这种提供服务的一般被称为机场
大多情况你们是找不到这种服务的,因为国家不允许明目张胆的宣传。
这是我用的机场,给你们的是我的邀请链接,https://hello-shudong.com/auth/register?code=y4jH 你们通过这个注册以后充钱是有返利给我的(不想返利给我可以用这个网站https://hello-shudong.com/auth/register)
要是返利多了我开个贵的多人套餐给我们一起用。你们也可以注册两个号,第一个邀请第二个然后返利就返给第一个号了反正40%的钱别浪费了
我也没有其他机场,总之翻墙是越来越难了你们可以私下交流互相要要
redis zset 数据结构
springcloud 各种组件
mysql 聚簇
java 类加载机制
hashmap 只有读有线程安全问题嘛
eruake和zk的区别
学习spring 首先要祭上spring 官方那张图
spring framework runtime
可以看出最核心的那个 是一个容器,IOC是spring最基本的能力,简单来说inversion of control,就是把软件的部分控制由程序员交给了spring ,最典型的就是对象的新建和初始化(依赖建立)的反转,除了解耦和减少程序员代码,spring的IOC还为其他的功能打下了基础。
core 提供了IOC过程中需要的“工具”,所有操作都需要它
context 上下文,在计算机系统中,进程执行时有进程上下文,如果进程在执行的过程中遇到了中断,CPU 会从用户态切换为内核态(当然这个过程用户进程是感知不到的,由硬件来实现的),此时进程处于的进程上下文会被切换到中断上下文中,从而可以根据中断号去执行相应的中断程序。
内容出处
Beans 存入到spring容器中的对象
AOP 核心概念和IOC一样非常好理解,就是抽取各部分中不重要或者重复的部分,拿出来(从侧面切出来)将原本程序从上到下执行逻辑中不重要的分离到侧面(常见的记录日志,权限控制)
AspectJ 是另一种 aop框架(貌似比spring AOP更强大?)原本的aop的编程方式太反人类了,所以spring后来借用了aspectj的方式,但核心还是spring
spring借用cglib而不是jdk自己的动态代理主要是应为要完成基于类的代理而不是为了啥效率
(1)JDK动态代理只能对实现了接口的类生成代理,而不能针对类
(2)CGLIB是针对类实现代理,主要是对指定的类生成一个子类,覆盖其中的方法
因为是继承,所以该类或方法最好不要声明成final
然而现在,直接ubuntu 或 wsl毕竟ubuntu背靠大公司,相比于其他发行版是最接近Windows稳定的(主要原因linux开 IDEA 比Windows快三倍)
试用了之后 感觉还不错
唯一的缺陷是终端实在太丑,
终端不好分屏。
好在主要使用idea 可以使用里面的终端,
vscode也搭好了,不过应该不用~ 也说不上。
接下来,装一下 后端主要的数据库 服务器 啥的。
x-client 主要提供图形界面的数据,也就是model
而 x-server 主要把界面画出来 安装了 vcXsvr 相当于用windows的api 把从ssh传过来的数据画成窗口
那么,我应该关掉虚拟机linux 里的 自带的 x-sever么
转念一想 微软应该已经想到这个了把
但是这一切,都可以用VM的方式来解决。我用的是hyper-v。把linux装到hyper-v里面,然后用ssh连接过去,windows本地起一个xserver,所有linux的图形界面都在windows上显示。 要启动VM,只要在powershell里打一句: start-vm ubuntu就行了。要建个快照 powershell里打一句: checkpoint-vm ubuntu,搞定。 基本上,要操作linux的所有东西都可以通过一个powershell命令搞定,不用麻烦的在GUI上点啊点。
但是这一切,都可以用VM的方式来解决。我用的是hyper-v。把linux装到hyper-v里面,然后用ssh连接过去,windows本地起一个xserver,所有linux的图形界面都在windows上显示。 要启动VM,只要在powershell里打一句: start-vm ubuntu就行了。要建个快照 powershell里打一句: checkpoint-vm ubuntu,搞定。 基本上,要操作linux的所有东西都可以通过一个powershell命令搞定,不用麻烦的在GUI上点啊点。
狗东买的8g内存到了,给敏宝的电脑加上,16g 加 不锁频的8550 性能应该可以,上次装完,试验了内存占用太大,这次继续
#4月23日
感觉 很多关于网络的东西不懂 看书先学习一下
大概主要学习完了
虚拟机上的网络三种
按 hyper-v 分
尝试了很很多次,安装了n遍 又仔细看了下知乎下面的评论 具体研究了下虚拟机下的网络
谷歌看了很多文章 突然看到百度一篇很有用的样子
参考博客
后续:服务器版本 装桌面环境太复杂,c盘空间不够。删了
经过两三天 n 小时的折腾 卡顿程度稍好于虚拟机,窗口拖动较友好
可能还是比不了真机,但真机实在不适合笔记本,先用着看,明天开始搭环境 web 开发走起
可能适合喜欢用键盘的人 鼠标可能还是不适合 还有输入中文有问题
一不小心 Ubuntu 重启后又不能联网了,正好我想把xserver关掉关不掉
,这回直接安装服务器版本
重装完又不行,折腾一两天,这次感觉终于弄懂了
- ifconfig 查看网卡信息
- vim /etc/etc/netplan/xxxx.yaml 开始编辑网络文件 注意目前只发现ubuntu用了netplan 其他的请自行谷歌啥怎么配网络
- sudo netplan apply
当多个分支同时,就会变成这样:
这种情况,git无法像刚刚一样,直接将master指针移向feature1那样,如果直接输入合并命令,就会合并失败
合并失败后git会将工作区冲突的文件变成这样:
<<<<<<< HEAD
ddd
=======
dev
>>>>>>> dev
修改过后再add commit一次 就会合并成功
就像这样:
多人协作是git的重要特性
如果 origin被其他人push过后,自己再想push就会有冲突,必须先pull pull后文件也会像这样:
<<<<<<< HEAD
System.out.println("dev");
System.out.println("fdsf");
=======
>>>>>>> d46723cb2f115382b802f7c02f9020beea4714cb
必须解决冲突后提交后再push
标签就是对那串哈希出来的码取的特殊名字
给当前版本,当前分支打标签:git tag v1.0
查看所有标签:git tag
给指定的commit id 打标签:git tag v0.9 f52c633
还可以创建带有说明的标签,用-a
指定标签名,-m
指定说明文字:
$ git tag -a v0.1 -m "version 0.1 released" 1094adb
用命令git show
可以看到说明文字:
$ git show v0.1
tag v0.1
Tagger: Michael Liao <[email protected]>
Date: Fri May 18 22:48:43 2018 +0800
version 0.1 released
commit 1094adb7b9b3807259d8cb349e7df1d4d6477073 (tag: v0.1)
Author: Michael Liao <[email protected]>
Date: Fri May 18 21:06:15 2018 +0800
append GPL
diff --git a/readme.txt b/readme.txt
...
使用唯一远程仓库进行多台电脑的远程开发方式就用以上方式,不过对于GitHub而言,这样必授权其他电脑拥有改写自己整个账号代码的权利,配合GitHub,如果我们想改其他人的代码,可以先fork下来,这样就变成你账号下的项目了,自己随心所欲修改后可以提交pull request,别人如果心情好,可能就采纳l
参考:[廖雪峰git教程](
介绍下实习项目 shiro 权限模型
rpc框架介绍
jvm调优(不会)
垃圾回收(忘了)
tcp 网络
算法题一亿条短信查找重复的最多的
我的缺点优点最自豪的事情
反问
三大主要硬件
软件分为操作系统(window,linux)和应用软件(如qq ,word)
操作系统是最底层的软件,他是应用软件和硬件连接的桥梁
文件的本质是磁盘上的一片数据,一块数据,是以二进制的方式存在硬盘里的,如果这个二进制符合某种编码(比如c语言里的ASCII)那这种文件被称为文本文件,你右键新建一个文本文件就是新建了一个二进制文件,而所有代码本身都是二进制文件的形式存在的(只不过改了后缀名 ,c语言的.c .cpp 网页的.html) 所以很多时候写代码我都会叫你新建一个.txt然后手动改成目标的后缀
另一种特殊的文件,符合操作系统的某种规范,所以可以双击直接运行,在windows里一般以.exe后缀,c语言本质就是把文本文件的代码编译成操作系统认的exe 应用程序
这个概念上课忘说了,文件只是文件,他自己是不可以打开自己的,任何文件的打开都是由另一个应用程序打开,比如双击.txt的文件是用记事本这个应用程序打开了这个文件,,,而记事本打开文件的规则就是用文本文件的方式编辑他,c编译器打开文件后的效果就是用c语言的语法编译它,浏览器打开文件就是用浏览器的方式渲染它
这个东西属于今晚临时想加的,重点就是解释下js这个语言可以用在哪。
我们用html的写了一个按钮,然后让点击这个按钮后可以执行一段js代码。
想学习html可参考网上教程
这里可以非正式的写一些代码,具体操作参考群文件
借助浏览器入门编程.pdf,用这个的目的只是为了便于演示的编程的基础(变量,函数 , if ,while),并不是直接推荐大家学习js这门语言。
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.