Code Monkey home page Code Monkey logo

qingshanyuluo.github.io's Introduction

  • 👋 Hi, I’m @qingshanyuluo
  • 👀 I’m interested in ...
  • 🌱 I’m currently learning ...
  • 💞️ I’m looking to collaborate on ...
  • 📫 How to reach me ...

qingshanyuluo.github.io's People

Contributors

qingshanyuluo avatar

Watchers

 avatar  avatar

qingshanyuluo.github.io's Issues

Hot 200解法快速记录-02

46. 全排列

mark数组有没有访问过,回溯递归,mark数组有优化的办法,但是很麻烦,算了

160. 相交链表

两个指针都分别从两条链表上都走过,这样速度就一样了

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;
}

54 螺旋数组遍历

            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; //重新设定左边界

92 反转链表指定一段

415. 字符串相加

142. 环形链表 II 寻找环起点

数学证明画图 分环前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;
    }
}

300. 最长递增子序列

动态规划dp数组记录当前位为末尾时最长递增,具体怎么找就是for循环前面的,找比当前位小的数字对应下标的dp[j]加一

42. 接雨水

最适合的方式是按列求并且动态规划,找出这列左右最高的柱子
先用两个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循环找出两边相对最矮的,减去当前柱子高度就是雨水量

143. 重排链表 寻找链表中点 + 链表逆序 + 合并链表

124. 二叉树中的最大路径和

深度遍历,返回值为树从上到下最高的和,左右两个加起来加根节点值即为路径和,注意的是,root的val一定要返回,在计算左右子树最大和的时候负数才会被忽略

19. 删除链表的倒数第 N 个结点

双指针,一个多走几步

72. 编辑距离

动态规划,二维数组,dp[i][j] 取 增删 改 三个最小的代价,两层for循环从1开始

232. 用栈实现队列

两个栈 当出栈为空的时候再将入栈塞过去

199. 二叉树的右视图

除了层序遍历,还可以深度优先 先中后右后左,这样可以保证每一层第一个访问到的是最右的。

56. 合并区间

先按左边界排序,然后遍历,如果遍历到左边界比结果右边界还大,就新开一个结果

70. 爬楼梯

p = q;
q = r;
r = p + q;

4. 寻找两个正序数组的中位数

搞个新数组,简单办法,非要log(n+m)就是二分,比如找第8大的数组 那两个数组中前4个,相对小的那个肯定能排除

加密 加盐

简单hash加密

哈希算法又称摘要算法,是一个多对一的映射,很多典型的例子如MD5,SHA-1

数据库中不能直接存密码的明文,否则一旦数据库被攻破,就可以登陆所有用户的账号,方法是存储用户口令的哈希,例如,MD5。在用户输入原始口令后,系统计算用户输入的原始口令的MD5并与数据库存储的MD5对比,如果一致,说明口令正确,否则,口令错误。

加盐哈希存储

简单的哈希加密并不能有效抵御上一种情况,虽然穷举很低效,但是如果做到一张对应表,那就可以直接算出来明文,为此,可以对明文密码加上一串随机数据,再进行hash算法,这样用表的方式也推不到原始的明文,然鹅,要验证输出的哈希,必须同时提供“认证码”。但是使用随机生成的盐也是有一定的弊端,较大的弊端就是,这个盐也必须存储,所以也是有机会获取的盐需要每个用户唯一,这样可以很大程度上防止查表,因为盐不同,即使知道盐是什么也无法生成数量如此庞大的表。可以使用128位的uuid来作为用户的盐,这样可以保证唯一性也可以保证盐的长度。
但是我个人更建议盐可以使用某种非公开的算法根据用户信息生成,而不是直接存储在数据表中的信息,即盐也需要保密。事实上盐最好也不公开,最简单的办法就是分为两部分,一部分为入参数据使用用户信息,这些存储于数据库,另外一部分使用生成算法,这些固化在代码中,不会随数据库沦陷而泄露。而且可以用盐反复加密,对方不知道你的代码逻辑,也很难破解。

java或者其他unix友好的应用开发方式

纯Windows

纯window下开发,有很多困难,普通的环境Windows配起来都很是费劲

Windows jvm python + 远程/虚拟机/wsl 组件

开始编程的时候大部分都是用这种方式,但由于windows火热的编程时代一去不复返,浏览器和移动端开始占据市场主流,在这个时代,linux的地位空前的高,大多数的后端服务器都运行在Linux平台,与此同时,开源的力量也变得空前的强大,考虑到大多数开源组件都是在Linux下先发布,同时对类unix系统的支持也比较好,例如mysql redis这些在linux比较好的组件就放在linux下运行,然后通过网络和本地windows的程序连接,达到开发的目的。

ssh+vscode远程开发

早就听说过命令行大佬写代码从不用图形界面,直接ssh连过去就写。然鹅vim等编辑器不经过费劲的折腾实在是比不上现代ide方便,同时学习成本也很高,感谢微软黑科技,vscode的remote development 现阶段已经完善到一定程度,通过ssh连接linux服务器,就可以获得一个类似与linux下打开的vscode,同时随着vscode插件的完善,现在直接用vscode已经可以很快乐的写c/c++ python nodejs,java也已经可以相当愉快的写了。

容器

以docker为代表的容器技术极大的方便了微服务下环境的搭建,只要是linux下一套环境,容器就可以将之打包,然后在任何装有docker下的机器上运行,类似于搬上去一整个虚拟机的运行环境。然鹅又没有虚拟机那么重量化,其实docker只是为你的运行组件隔离出来了一个环境,其底层还是用本身操作系统,这也就是为啥docker不能虚拟出一个windows环境一样,应为虚拟出来的环境本质还是用同一套内核的。

虚拟机

买不起云服务器的同学可以使用虚拟机替代,出了占用本机资源这一个缺点,虚拟机相比于自己花那一点小钱买的云服务器拥有更高的性能,同时和本机的连接不会受外部网路影响,同时你将会学到很多网络的知识

Hot200 算法记录 -05

翻转二叉树 简单,只要不中序换就行了

14. 最长公共前缀

从前往后比就行了

162. 寻找峰值

二分,往上赶,上升那边一定有个峰值,然后选右值,每次也和+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;
    }
}

62. 不同路径

动态规划,比较传统dp[i][j] = dp[i-1][j]+dp[i][j-1];

128. 最长连续序列

需要o(n)解法,本质就是去重先放哈希set里,然后从前往后遍历,对每个都链起来,可以从小到大找,

83. 删除排序链表中的重复元素

用个虚假头指针
比 next 和next.next

227. 基本计算器 II

这题得用栈,

695. 岛屿的最大面积

深度优先搜索 mark数组 可以用过置为0

198. 打家劫舍

隔一个或者隔两个最大的 动态规划 一维dp数组即可

122. 买卖股票的最佳时机 II

一比较简单,直接记录每一位前面最小价格,
二也比较简单,直接记录上升,忽略下降
三和四是最多两笔和最多k比,就要多个dp数组了,

152. 乘积最大子数组

两个dp数组,一个存最大,一个存最小,因为负负得正

153. 寻找旋转排序数组中的最小值

二分,确定哪边有序后按范围筛

179. 最大数

先排序,按啥排序呢,就是a+b大还是b+a大,拼接,然后大的放前面,拼起来 怎么拼就是比如999和88先让999*100加88

283. 移动零

双指针,一个保证有效

Mybatis学习

Resources.getResourceAsStream(resource)源码探究:

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


SqlSessionFactoryBuilder

这个类可以被实例化,使用和抛出,在你创造了你的SqlSessionFactory后它就没必要继续被保持了,因此SqlSessionFactoryBuilder实例的最佳的域是方法域(比如作为一个本地方法的变量),你依然可以保留SqlSessionFactoryBuilder用以创造多个的SqlSessionFactory实例,但是为了所有和XML语法分析的资源得以释放去做更重要的事,我们最好还是不要保留他.

SqlSessionFactory

一旦被创建,SqlSessionFactory就应该在你整个应用执行期间存在,我们甚至几乎都没有理由去销毁或者重新创造它,在一个应用运行期间不重新构建SqlSessionFactory多次是一个最佳的惯例,而不这样做通常会被人们认为是个坏事情.因此最好的SqlSessionFactory域应该是application域,有很多种方式可以做到这样,最简单的就是用一个单例模式或者静态单例模式。

SqlSession

每个线程应该拥有它自己的SqlSession实例,SqlSession的实例是不被线程共享的,也是线程不安全的,因此对它来说最好的域是方法域和请求域,永远不要将一个SqlSession实例的引用放在一个静态字段甚至一个类的实例字段中,也永远不要放在任何形式的被管理的域比如Servlet framework的HttpSession中,如果你用了任何形式的web框架,考虑把SqlSession放在一个和HTTP 请求差不多的域中,用另一句话来说就是当收到一个HTTP请求,你就可以开一个SqlSession,然后当返回这个相应的时候,你可以关闭它,关闭这个回话是非常重要的,你必须永远确保会话被关闭在一个finally块,下面是确保会话被关闭的标准模式写法:

try (SqlSession session = sqlSessionFactory.openSession()) {  // do work}

Mapper Instances

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排除)

vue.config.js

一个js文件,是自己新加的,vuecli应该认识,可以读取里面的设置

src/

代码

public/

favicon和一个基础的html内容提示:
We're sorry but wuliu doesn't work properly without JavaScript enabled. Please enable it to continue
还有个id=app的div

package.js&package-lock.json

package.json 这个文件是 npm init 时创建的一个文件,会记录当前整个项目中的一些基础信息。而 package-lock.json 这个文件却是 node_modules 文件夹或者 package.json 文件发生变化时自动生成的。这个文件主要功能是确定当前安装的包的依赖,以便后续重新安装的时候生成相同的依赖,而忽略项目开发过程中有些依赖已经发生的更新。

node_modules

安装项目域的node模块

babel

babel官网正中间一行黄色大字写着“babel is a javascript compiler”,翻译一下就是babel是一个javascript转译器。为什么会有babel存在呢?原因是javascript在不断的发展,但是浏览器的发展速度跟不上。以es6为例,es6中为javascript增加了箭头函数、块级作用域等新的语法和Symbol、Promise等新的数据类型,但是这些语法和数据类型并不能够马上被现在的浏览器全部支持,为了能在现有的浏览器上使用js新的语法和新的数据类型,就需要使用一个转译器,将javascript中新增的特性转为现代浏览器能理解的形式。babel就是做这个方面的转化工作。

src

.
├── 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
  }
}

Vue的学习可以查看官方文档

说几个要点:

  1. mvvm
    MVVM是一种设计**。Model 层代表数据模型,也可以在Model中定义数据修改和操作的业务逻辑;View 代表UI 组件,它负责将数据模型转化成UI 展现出来,ViewModel 是一个同步View 和 Model的对象,是业务逻辑层(一切js可视业务逻辑,比如表单按钮提交,自定义事件的注册和处理逻辑都在viewmodel里面负责监控俩边的数据)在MVVM架构下,View 和 Model 之间并没有直接的联系,而是通过ViewModel进行交互,Model 和 ViewModel 之间的交互是双向的, 因此View 数据的变化会同步到Model中,而Model 数据的变化也会立即反应到View 上。这种自动同步是因为ViewModel中的属性实现了Observer,当属性变更时都能触发对应的操作。ViewModel 通过双向数据绑定把 View 层和 Model 层连接了起来,而View 和 Model 之间的同步工作完全是自动的,无需人为干涉,因此开发者只需关注业务逻辑,不需要手动操作DOM, 不需要关注数据状态的同步问题,复杂的数据状态维护完全由 MVVM 来统一管理。
  2. 组件化 组件化是开始就吸引我的,保证代码的重用性,使得前端开发现代化。

element ui

ui框架使得我的开发变得很容易,现在基本上回不去了,是单体开发的重要快捷方法,文档易懂

axio

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的控制台,

http post

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在前面

项目代码

Java中的日志

Log4j

Log4j有7种不同的log级别,按照等级从低到高依次为:TRACE、DEBUG、INFO、WARN、ERROR、FATAL、OFF。如果配置为OFF级别,表示关闭log .

log4j也很久没有更新了,现在已经有很多其他的日志框架对Log4j进行了改良,比如说SLF4J、Logback等

Log4j2

  1. 插件式结构。Log4j 2支持插件式结构。我们可以根据自己的需要自行扩展Log4j 2. 我们可以实现自己的appender、logger、filter。
      2. 配置文件优化。在配置文件中可以引用属性,还可以直接替代或传递到组件。而且支持json格式的配置文件。不像其他的日志框架,它在重新配置的时候不会丢失之前的日志文件。
      3. Java 5的并发性。Log4j 2利用Java 5中的并发特性支持,尽可能地执行最低层次的加锁。解决了在log4j 1.x中存留的死锁的问题。
  2. 异步logger。Log4j 2是基于LMAX Disruptor库的。在多线程的场景下,和已有的日志框架相比,异步的logger拥有10倍左右的效率提升。

logback

springboot 默认实现

slf4j

一个统一的接口

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提供了统一的记录日志的接口,只要按照其提供的方法记录即可,最终日志的格式、记录级别、输出方式等通过具体日志系统的配置来实现,因此可以在应用中灵活切换日志系统。

jsr330 jsr250

注解:

javax.annotation下:

  • @generated 标记这段代码不是手敲的,是自动生成的
  • @ManagedBean 标记这个pojo已经被管理 jsf中定义
  • @PostConstruct : 标注bean的初始化工作函数
  • @PreDestroy : 标注bean的销毁工作函数
  • @priority :标记在类或参数上,表示这个类被注入时的优先级
  • @resource 大多数情况下等价于spring内置的注解@Autowired 表示应用需要这个资源
  • @Resources 只能标记与类上,需要多个资源

javax.inject下

  • @Inject 类似@resource @resource是java中JSR250中的规范,@Inject是java中JSR330中的规范
  • @nAmed 类似@ManagedBean cdi 中定义 具体不太懂
  • @qualifier 自动注入的时候,使用了这个注解,那么在候选bean中,就会用这个注解指定的那个bean,作用在字段,方法,类,参数,注解上
  • @scope 表作用域
  • @singleton 继承 @scope 表示ioc容器内只创建一次,且生命伴随整个ioc容器

wsl介绍 及 配合vscode

先决条件:
装好wsl 无论 1和2 参考微软官方教程或自行百度
PS: 微软官方文档质量超高

wsl1和 wsl2 的区别

  • wsl1 基于api翻译 说人话,不同系统的程序不能通用的原因就是读不懂系统的指令比如编程的时候windows读文件a函数,linux是b函数,这样不同系统就不能通用,而wsl 1就是通过 翻译来让我们拥有一个linux环境的
  • wsl2 其实就是虚拟机了但是微软给了交互性上,性能上的优化,几乎可以代替linux所有操作
  • 如何选择:建议有能力升级下wsl2 主要是io性能大幅提升(学js node相关技术的时候有大用)

ubuntu linux各种发行版区别介绍

vim编辑器使用

Linux基本指令

路径的概念

以斜杠开头 表示 文件夹嵌套关系的一个路径

windows下一般这样:C:\Users\h-l-j

linux : /home/

  • ls 列出当前目录下所有文件和文件夹
  • cd “cd 文件夹名字" 是进去到这文件夹里,当前路径也会变化
  • mkdir "mkdir 希望新建的文件夹的名字" 新建这个文件夹
  • touch "touch 文件名" 新建文件(ps 文件和文件夹区分清楚哈!!!)

更改软件源

  1. sudo vim /etc/apt/sources.list

  2. 进入vim后 按下冒号(就是打出一个":",英语输入法)

  3. 输入
    image

  4. 回车

  5. 按下冒号: 输入 x 回车

  6. 更新软件源 sudo apt update

安装c语言环境

linux 的另一个好处是安装啥语言都一个命令就安装完了(以下命令只适用ubuntu,其他linux请自行查找)

  • 安装c语言: sudo apt install gcc g++ gdb
  • 安装python sudo apt install python3 (不过不用了。linux一般自带python)
  • 安装java sudo apt install openjdk-11-jdk
  • 安装 nodejs sudo apt install nodejs

vscode 操作

ctrl + `(tab上方) 打开命令行/shell

插件 打开各种语言都会推荐你,可以自行探索

snippet 可以装逼

vscode 配合wsl 食用

  • 首先打开vscode 装 wsl 插件

  • 然后linux 新建一个ccc 目录

  • 用 code ccc的命令打开这个目录

  • 新建 .c 或者 .cpp结尾的文件,

  • 按照提示安装c的插件(网络原因卡了可重开)

  • ctrl + `(tab上方) 打开命令行/shell

    • 输入 gcc 你的文件名编译
    • 输入 ./a.out 运行

vscode debug

按下 f5 一路回车,出现新文件卡住了就关了再 f5
实在不行删除这个文件夹重来
image

网络

概述

近期学习计算机网路,按目前认知先大概地分一下层

  1. 首先以网卡来一分为二,上面的都是软件实现的,网卡根据mac地址发送给下一个网卡

  2. 讨论一下 网卡以上的,参考《网络是如何连接的》 自认为用模块 这个概念比较合适,

    毕竟以我看来,这些都是一个个软件(层),对数据进行封装(整理,添加某些的东西)

    这里我们举个例子:

    • 网页本身是 按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 部分 又要加点东西,,,类似上面的

  3. 加完这些东西,把这个数据 丢给网卡,网卡按照上面的mac地址发个和它相连的网卡

  4. 90年代,以太网取得垄断地位,以太网成为局域网代名词。
    数据链路层
    仅需要MAC层,采用以太网帧格式(Ethernet V2封装:ARPA).

  5. 最底层的电路层,具体谈论传输介质啊,方式啥的,比如无线电波,光缆啥的

    主要就是数字信号的发送

    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原理and调优

jvm相信大家都了解,一个jvm进程对应一个操作系统进程,而在hotspot虚拟机里一个java线程也和操作系统的线程一一对应
当线程本地存储、缓
冲区分配、同步对象、栈、程序计数器等准备好以后,就会创建一个操作系统原生线程。
Java 线程结束,原生线程随之被回收。操作系统负责调度所有线程,并把它们分配到任何可
用的 CPU 上。当原生线程初始化完毕,就会调用 Java 线程的 run() 方法。当线程结束时,
13/04/2018 Page 21 of 283
会释放原生线程和 Java 线程的所有资源。

jvm 原生后台进程:

  • 虚拟机线程 (VM thread) 这个线程等待 JVM 到达安全点操作出现。这些操作必须要在独立的线程里执行,因为当 堆修改无法进行时,线程都需要 JVM 位于安全点。这些操作的类型有:stop-theworld 垃圾回收、线程栈 dump、线程暂停、线程偏向锁(biased locking)解除。

  • 周期性任务线程 这线程负责定时器事件(也就是中断),用来调度周期性操作的执行。

  • GC 线程 这些线程支持 JVM 中不同的垃圾回收活动。

  • 编译器线程 这些线程在运行时将字节码动态编译成本地平台相关的机器码。

  • 信号分发线程 这个线程接收发送到 JVM 的信号并调用适当的 JVM 方法处理。

jvm内存结构:

image
image
image

jvm调优

gc原理

gc相关算法

10种垃圾回收器

jvm检测工具and 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是一种编程范式用来增加程序的模块化通过横切关联点的分类(不是人话)。解释一下:传统的业务逻辑是从头到尾一块一块执行的,将整块的逻辑分类出核心和非核心(横切)。

动态代理工具

  1. 手动拼接字符串保存为.java文件,其中代理部分通过反射获取各个通知等转换成的字符串,再收到将源代码编译成.class,再通过自定义的classloader加载进来,具体见博客通过代码拼接实现动态代理(拼接字符串,再编译完全模仿了人类手动代理的方式,效率较低,不推荐仅作演示)
  2. ASM提供一套API操作java中类似汇编的成分,不过需要使用者能读懂一些java字节码层面的东西,跳过了源代码编译成字节码,效率较高
  3. javassist提供一套api通过易于理解的方式生存字节码
  4. jdk动态代理:基于接口实现,创建一个代理类JDKDynamicProxy实现java.lang.reflect.InvocationHandler接口,https://www.cnblogs.com/zuidongfeng/p/8735241.html
  5. cglib动态代理https://blog.csdn.net/yhl_jxy/article/details/80633194

spring-toy

在这个框架的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 等著名框架,为了“非侵入性”原则,都会使用动态代理。理解动态代理,在很多使用框架的时候就可以了解其内部原理。

Hot 200 算法记录 -03

148. 排序链表

归并排序,先用快慢指针找到中点后断开,对两边分别排序,再merge一下两个结果

31. 下一个排列

就是找字典序的下一个排列,如果是完全递减的,就直接反转成递增的,然后 为了影响范围足够小,从后往前找到第一个递增的。然后把这个递增的前一个数字替换掉,具体就是后面从后往前遍历找一个比这个数字大的和这个互换,然后对后面的做升序排序,只要反转即可。

82. 删除排序链表中的重复元素 II

直接dum指针是假头,然后 cur cur.next cur.next.next 直接进行删

1143. 最长公共子序列

动态规划,子序列不是子串,可以不连续,然后为了防止i-1小于下标,所以dp[][]每个维度长度都长一位,如果i j的字母相同 dp[i][j] = dp[i-1][j-1],如果不相同,就是比较 dp[i][j-1] 和 dp[i-1][j] 哪个大,

69. x 的平方根

二分查找 比较在哪两个整数中间

2. 两数相加

直接相加

93. 复原 IP 地址

本身没啥难的,但是很复杂

自己生成parseInt

括号生成

78 子集

这题有个核心**是从前往后遍历。当前位置选不选都有两种可能,然后分别递归到下一位

155 最小栈

用个辅助栈存当前位置最小的

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();
    }
}

165. 比较版本号

字符串分割后存下,不然就从前往后遍历,到. 每段比

93. 复原 IP 地址

这题写起来贼复杂
核心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;
            }
        }
    }

105. 从前序与中序遍历序列构造二叉树

经典题

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个就是左子树的,后几个就是右子树的

322. 零钱兑换

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为结尾的影响

76. 最小覆盖子串 难 麻烦

滑动窗口,两个指针如何判断区间包含呢。用一个哈希表记录每个元素出现的次数。和另一个哈希表记录目标字符串的各个元素数量比较,代码毕竟复杂

41. 缺失的第一个正数

这题首先把所有负数都改成+999999最大的正数。难点在于不利用额外空间。然后,可以利用数组本身作为哈希表,但是又要求不要用额外空间,之前把所有负数都变成正数就是为了用正负来打标签,从前往后遍历,数组第i位有就变负数,等于用负数来作为boolean

239 滑动窗口最大值,

单调队列
队列记录下标
本质就是维护一个递减的队列,为了首位超出范围后能直接使用后一位,所以每次都从后往前遍历,把比自己小的全去掉。第二步就是判断首位在不在范围内,不在也去掉,第三步取首位
记住几个linkedlist 的方法,addFirst peek poll addLast peekLast

springcloud入门


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)数据中心,云计算等和管理平台。

Features

Spring Cloud focuses on providing good out of box experience for typical use cases and extensibility mechanism to cover others.

  • Distributed/versioned configuration 【分布式以及版本化的配置】
  • Service registration and discovery【服务注册与发现】
  • Routing【路由】
  • Service-to-service calls【服务调用】
  • Load balancing【负载均衡】
  • Circuit Breakers【断路器】
  • Global locks
  • Leadership election and cluster state
  • Distributed messaging【分布式消息】

服务注册与发现

统一化的管理,可以实现各个微服务间良好的解耦,服务提供者和消费者都注册到Eureka服务器上,浏览器访问消费者地址,消费者会通过Eureka服务器上服务提供者的名字,找到提供者的真实路径,进行调用。高可用时可以搭建几个Eureka服务器,互相注册,复制各自信息。

贴一下关键代码:

eureka server:

项目开始前更改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

参考教程: 使用Spring Cloud与Docker实战微服务

流行的几门编程语言

前言

计算机发展这么多年,流行的语言在其各自擅长的领域都是不可替代的存在;同时在前几门语言中,我们也没有看到两个定位相似的语言。排名前几的语言没有高下之分。

java

常年排名第一,岗位也是最多的,虽然理论上适合干几乎所有领域,但现在其实也就在服务器端称王了,其他领域还是弟弟,其严格的类型约束,良好的面向对象支持,GC机制和不错的运行速度很适合大型超大型的项目开发,再加上在服务器领域深耕多年,生态完整,所以很适合干后端。(其实gui开发也是他擅长的,另一门和java很相似的语言c#就很适合开发windows桌面应用,安卓也是java开发,java自带的gui框架swingjavaFX在上个时代还是很流行的,谷歌新的gui框架flutter用的也是一门和java类似的语言dart,但总结,java的gui要么被类似的替代了,要么过时了)

python

两个特点

  • 简单 语法简单,给python设计的各种api也简单(这是我的感觉)
  • 库多,啥领域的都有,图形界面开发,后端,科学计算,人工智能,爬虫等等
    (PS:python应该是主力语言中最慢的的了哈哈哈,一方面解释型语言本来就慢,另一方面,另一个主流的解释型语言js有谷歌大大开发的V8引擎,贼快哈哈哈,不够python更多时候是扮演一个指挥者的角色,慢不慢不重要)

c

c太过底层,不支持面向对象,主要就是在嵌入式那边开发底层驱动,或者写个操作系统内核(linux内核就是c写的,据说如果用c++重写可以减少四分之三的代码量,但是Linus就是不这样,怕c++有坑)

c++

c++不只是c的面向对象版本,事实上他太大而全了,啥都能写,而且任何一个程序员都可以按照自己的喜好把c++写的像另一门语言(在我接触的Qt给我的感觉就是设计的很像java),但这是一门为“聪明人”设计的语言,而大多数人“不聪明”。所以我认为这在很多情况下不是门"好语言",以前的c++可谓个方面通吃,现在

js

就是网页了,B/S架构越来越流行,而浏览器里除了js几乎不可能出现第二种语言,并且这几年js有向外的发展趋势(app,后端),(js语法有很多坑,这几年的es5 6 7和typescript就在填坑)

大学阶段

毫无疑问这几门应该都要会,c/c++主要作用在于完成老师作业和刷算法题
python在爬数据,写一些小脚本方面很有用(主要是库多)
java 和 js 作为互联网中几乎占据**地位的语言工作岗位最多

Hot 200解法快速记录-01

3. 无重复字符的最长子串

双指针滑动窗口

146 LRU缓存设计

自己搞一个+HashMap + 双端链表, 不要用linkedlist ,然后实现这个双端链表的更新一个数据到头部的方法(刷新最近使用),记录链表的节点数,超了就踢掉最后一个节点

215 数组中第k大的元素

类似快速排序 ,但是只要管k所在侧的排序

25 k个一组反转链表

用循环第一次访问到第k个将第k个断开,然后用循环再反转这个k个。因为上次断开了

15 三数之和

先排序 从小到大,然后定住第一个值,对后面的数字进行一左一右双指针往中间移。如果重复就跳过

53 最大子序和

动态规划, dp[i]的含义为包含第i位的最大值,从前往后遍历即可

手撕快排

合并两个有序链表(简单)直接递归 比较大小

两数之和 hash

二叉树层序遍历 用队列

搜索选择有序数组

三指针二分 判断mid指针是否大于nums[0] 确定哪边有序,然后判断数是否在这个nums[mid] 和 头 或尾范围内

有效的括号(左右计数 或者栈)

最长回文字串

中心点往外扩散,要注意中心的可能是个缝,回文子串是个双数

121 买卖股票最佳时间(买卖一次)

显然,如果我们真的在买卖股票,我们肯定会想:如果我是在历史最低点买的股票就好了!太好了,在题目中,我们只要用一个变量记录一个历史最低价格 minprice,我们就可以假设自己的股票是在那天买的。那么我们在第 i 天卖出股票能得到的利润就是 prices[i] - minprice。

岛屿数量

mark[][]数组标记 两层for循环 深度优先搜索,直接向四周扩散

环形链表

判断有环 双指针 快慢 相遇 找环起点 双指针 相遇后另一个指针从头开始,再等相遇

二叉树锯齿形遍历

广度优先一样,用队列,一边从左到右,一下从右到左

二叉树的最近公共祖先

如果是二叉搜索树,那可以安装搜索树的特性找到两个节点,然后通过记录父节点列表,找到父节点列表里的最近一样的
如果普通二叉树,那就是递归查找两个值,返回能否找到两个值中的一个,如果一个root左右两个分别都能找到,那肯定是一左一右,那就是最近公共的

Hot200 快速解题-04

32. 最长有效括号 难

动态规划,

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;
    }
}

104. 二叉树的最大深度

简单

110. 平衡二叉树

简单

101. 对称二叉树

简单

129. 求根节点到叶节点数字之和

最简单的是深度优先遍历,本质是回溯,上层传给下层上层的和,到叶子节点了返回回去,然后再返回值全加起来

543. 二叉树的直径

左加右加起来算直径 和 然后dfs返回的是左右最深的加一

113. 路径总和 II

简简单单,深度遍历,找个list存一下

98. 验证二叉搜索树

中序遍历递归

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;
    }

64. 最小路径和

动态规划 注意两个第一行特殊考虑

470. 用 Rand7() 实现 Rand10()

注意不能两个rand7加起来 因为对于14 只有一种组合 对于5 就有 2+3或者3+2两种了
核心是既然是rand7那就是七机制,7进制一位不大于十那就两位,这样就是49以下的数了。然后保证40以下 ,再➗10

112. 路径总和 简单

48. 旋转图像

原地旋转,比较麻烦,核心分清楚奇数还是偶数,如果偶数好办,所以两个循环条件一个是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;
            }
        }
    }
}

234. 回文链表

还算简单。不用额外空间可以先快慢指针找到中点后后半段反转

39. 组合总和,还有一题 二 区别在于能否重复使用元素

这道比较简单,直接回溯 ,两种情况,选当前还是不选,然后传一个还剩多少额度
另一题比较复杂,还要考虑重复,所以要首先排序

169. 多数元素

简单就是统计,时间复杂度最低也要o(n)
有个不需要额外空间的算法,投票算法 消消乐杀敌,可以用个count

34. 在排序数组中查找元素的第一个和最后一个位置

就普通的二分查找后向前向后扩展

221. 最大正方形

动态规划,先初始化第一行第一列,然后选左和上和左上三个中最小的正方形
min(dp(i - 1, j), dp(i, j - 1), dp(i - 1, j - 1)) + 1;

718. 最长重复子数组

动态规划,dp[][]含义为以各自下标为结尾的最长重复

字符串解码,

这题和解码ip一样都是很麻烦的题

wego 第二次上课纲要

shell的概念

shell是操作系统中人机交互最重要的一个东西,翻译为壳,意思是包住操作系统内核的壳
你们windows不管是打开我的电脑还是任何一个文件夹跳出来的那个被称为文件资源管理器的东西就是windows的shell

一个shell的主要作用

  • 浏览当前目录下的子目录和文件
  • 切换目录
  • 打开应用程序/文件

图形化vs命令行

我们现在用的操作系统大多都是有图形化的界面的,这个得感谢乔布斯和比尔的推广(微软公司和苹果公司)
早期的电脑都是没有图像界面只有命令行的(样子可参考打开cmd然后全屏)
对于用户来说,自然是图像化的比命令行好理解好操作
但是对于程序员来说,当然是命令行的程序好写(程序员省点力,用户就多花点力气,反过来。。。反过来)
你们现在应该还没人接触到gui的编写,你们的c语言也是跳出一个黑框框

powershell or cmd 的使用

  • cd
  • ls
  • 文件路径

引入linux和wsl

linux 或者说 unix 是目前it界占据大半江山的操作系统
linux系统的学习可以说是程序员的一条必经之路

简单讲解linux命令和使用方法

使用wsl配合vscode创建c项目

讲解c语言(如果时间来得及)

附加非正式内容

  • 自我介绍
  • 分组互相认识
  • 讲讲gui和命令行的前生今世哲学
  • 分享推荐方向前端

操作系统入门

操作系统 导言

此文 为 《现代操作系统》 第一章 “引论”
的归纳总结,主要 分为两个部分:

  • 硬件和软件

稍加补充:

  • 小概念
  • 我的小感悟(待补充)

计算机硬件

处理器 processor

  1. 指令集的概念 x86 arm 可以理解为汇编的 指令 比如 取出一个数,加,自增
  2. 寄存器
    1. 变量和临时结果的通用寄存器
    2. 序员可见的专用寄存器 for example
      1. 程序counter 保留下一条指令的地址
      2. stack pointer 内存中当前栈的顶部
      3. program status word PSW 包括条件码位 cpu 优先级 模式(用户态,内核态)等等各种控制的 通常 一下子全读取 但只用其中一小部分,在I/O的时候 作用很重要
    1. 流水线:取指 -> 解码 -> 执行 前后都在动
    2. 多超标量 cpu : (取指 -> 解码)* n (缓存区) ==> 执行
  3. 内核态 用户态 用户态很多操作是禁止的 执行的指令集是全部的一个子集 为了从操作系统中获得服务,(system call)用户程序必须用(TRAP) 指令 切换
  4. 多线程 在纳秒间来回切换 类似多个cpu 还有 多核(需要特别操作系统)

储存器 memory

  1. cash
  2. 主存 random access memory
  3. rom 便宜 速度快 引导模块 I/O卡
  4. Electrically Erasable PROM 电可擦除可编程ROM 和 flash memory 非易失性,写的速度比 rom 慢 多次擦写 损耗
  5. cmos 计算机时钟 关机也能正确更新 小电池 用cmos很省电

磁盘

I/O

  • 控制器
    复杂操作 小型计算机

  • 设备本身
    规范化的接口 STAT

  • 驱动 内核态/用户态

  • cpu 中断 阻塞

交给萌新们的科学的上网的指南

科学上网,又称翻墙
主要是某些网站他不遵守**法律,所以我国政府把他禁了,当然变相的也保护了本地企业。其实我是支持**政府这个行为的,可以有效防止**舆论权掌握在海外反华力量手中,此处不做过多讨论。总之虽然这一政策给我们程序员带来了巨大的不便,但我个人还是支持国家的这一行为的。

翻墙原理

本质就是在国外(也可能是**香港)有一台电脑,他代替你访问那些被禁掉的网站,然后把数据发给你。所以问题就在于要有这样一台在国外的机器给你提供服务。

免费or收费

曾经我也是个白嫖党,但是免费的不好找,找到了还不稳定,稳定了还一两天就不能用了,所以后来我也花钱了,找了个最便宜的一个月五块钱,再也不担心不费事了。这种提供服务的一般被称为机场

找不到

大多情况你们是找不到这种服务的,因为国家不允许明目张胆的宣传。
image
这是我用的机场,给你们的是我的邀请链接,https://hello-shudong.com/auth/register?code=y4jH 你们通过这个注册以后充钱是有返利给我的(不想返利给我可以用这个网站https://hello-shudong.com/auth/register)
要是返利多了我开个贵的多人套餐给我们一起用。你们也可以注册两个号,第一个邀请第二个然后返利就返给第一个号了反正40%的钱别浪费了

我也没有其他机场,总之翻墙是越来越难了你们可以私下交流互相要要

对了对了,有时候我这个也访问不了,你们可以热点宽带换换都试试,如果都不行那只能是无缘了

最后,我想说,现在要是让我不能翻墙,我可能直接没法活了hhhh

腾讯二次一面漏经

redis zset 数据结构
springcloud 各种组件
mysql 聚簇
java 类加载机制
hashmap 只有读有线程安全问题嘛
eruake和zk的区别

浅谈spring

老生长谈 IOC

学习spring 首先要祭上spring 官方那张图

spring framework runtime

spring framework runtime

可以看出最核心的那个 是一个容器,IOC是spring最基本的能力,简单来说inversion of control,就是把软件的部分控制由程序员交给了spring ,最典型的就是对象的新建和初始化(依赖建立)的反转,除了解耦和减少程序员代码,spring的IOC还为其他的功能打下了基础。

粗略的组件理解

  1. core 提供了IOC过程中需要的“工具”,所有操作都需要它

  2. context 上下文,在计算机系统中,进程执行时有进程上下文,如果进程在执行的过程中遇到了中断,CPU 会从用户态切换为内核态(当然这个过程用户进程是感知不到的,由硬件来实现的),此时进程处于的进程上下文会被切换到中断上下文中,从而可以根据中断号去执行相应的中断程序。

    内容出处

    • DefaultListableBeanFactory
      这就是大家常说的 ioc 容器,它里面有很多 map、list。spring 帮我们创建的 singleton 类型的 bean 就存放在其中一个 map 中。我们定义的监听器(ApplicationListener)也被放到一个 Set 集合中
    • BeanDefinitionRegistry
      把一个 BeanDefinition 放到 beanDefinitionMap。
    • AnnotatedBeanDefinitionReader
      针对 AnnotationConfigApplicationContext 而言。一个 BeanDefinition 读取器。
    • 扩展点集合
      存放 spring 扩展点(主要是 BeanFactoryPostProcessor、BeanPostProcessor)接口的 list 集合。
  3. Beans 存入到spring容器中的对象

AOP

  • AOP 核心概念和IOC一样非常好理解,就是抽取各部分中不重要或者重复的部分,拿出来(从侧面切出来)将原本程序从上到下执行逻辑中不重要的分离到侧面(常见的记录日志,权限控制)

  • AspectJ 是另一种 aop框架(貌似比spring AOP更强大?)原本的aop的编程方式太反人类了,所以spring后来借用了aspectj的方式,但核心还是spring

  • spring借用cglib而不是jdk自己的动态代理主要是应为要完成基于类的代理而不是为了啥效率

    (1)JDK动态代理只能对实现了接口的类生成代理,而不能针对类
    (2)CGLIB是针对类实现代理,主要是对指定的类生成一个子类,覆盖其中的方法
    因为是继承,所以该类或方法最好不要声明成final

remote x11

然而现在,直接ubuntu 或 wsl毕竟ubuntu背靠大公司,相比于其他发行版是最接近Windows稳定的(主要原因linux开 IDEA 比Windows快三倍)

方法概述

  1. 在hyper-v中建立一个乌班图
  2. 分配网卡,在windows 适配器管理里设置共享网络
  3. 可以上网以后安装openssh 开启ssh 服务
  4. windows 上 安装 vcXsvr 后台开启x-server服务 地址为 localhost:0.0
  5. 打开putty 设置x11
  6. 连接 虚拟机后 命令行 idea 便可开启 intellj idea 程序

评价

试用了之后 感觉还不错
唯一的缺陷是终端实在太丑,
终端不好分屏。
好在主要使用idea 可以使用里面的终端,
vscode也搭好了,不过应该不用~ 也说不上。
接下来,装一下 后端主要的数据库 服务器 啥的。

x-client 和 x-server

x-client 主要提供图形界面的数据,也就是model
而 x-server 主要把界面画出来 安装了 vcXsvr 相当于用windows的api 把从ssh传过来的数据画成窗口
那么,我应该关掉虚拟机linux 里的 自带的 x-sever么
转念一想 微软应该已经想到这个了把

详细方法 之后补上

界面展示:

img

img

img

ubuntu 作为日常开发

构建后端终极开发环境

参照

知乎大神

但是这一切,都可以用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上点啊点。

知乎大神的步骤

  1. 首先在windows 功能中激活WSL windows subsystem for Linux 和 hyper-V。 注意hyper-v 需要win10 pro版才有。请想办法升级到pro版。
  2. 等上述功能安装完成后,下载ubuntu LTS的iso,我用的是16.04LTS,最近18也出了,选择你喜欢用版本,下载下来。
  3. 在开始菜单中搜索hyper-v打开hyper-v控制器
  4. 在hyper-v中打开虚拟交换机管理器,之前应该只有一个默认连接,按你的需求建立好网络连接,我一般会另外建立一个host only的网络,用于windows host与vm进行通讯。
  5. 建好后,新建一个叫ubuntu的VM,设置好网络,内存硬盘等,如果可能的话,尽量给它16G以上的内存。在VM中安装好ubuntu和各种vm-tools,以及sshd,同时配置好host only 的网络,使用staticIP。关掉hyper-v管理器,之后我们不再需要它了。之后所有的操作都可以用powershell进行了。
  6. 下载安装Xserver,我用的是vcXsvr,有人反应不够稳定,你可以选择你喜欢的,如cygwin/X等。 启动你的xserver并禁用host control。
  7. 按windows+r,输入 bash,进入WSL,第一次运行会安装一点东西。启动完成后用vi修改一下你的 ~/.bashrc文件,加入DISPLAY的设置,如export DISPLAY=localhost:0.0, 试试能不能启动glxgears,并用xhost +禁用access control。
  8. 现在可以ssh -X 到你的VM了。 在你的VM ssh中试着输入xterm,这时候你应该能看到一个xterm在你的windows桌面显示出来。这就表示环境配置完成了。

操纵VM,可以直接在hyper-v中操作,但是更方便的方法是用powershell,按win+x a,打开一个powershell, 常用的命令列在下面,基本所有的操作都可以用powershell进行操作。

hyper-v文档

hyper-v里的X还是蛮卡的,用X Forwarding之后会流畅很多。你可以试一下下。

4月18日

狗东买的8g内存到了,给敏宝的电脑加上,16g 加 不锁频的8550 性能应该可以,上次装完,试验了内存占用太大,这次继续

#4月23日

关于linux 虚拟机网络

感觉 很多关于网络的东西不懂 看书先学习一下
大概主要学习完了
虚拟机上的网络三种
按 hyper-v 分

  • 内部 nat 网络地址转化,我设置了共享就可以上网了,是一个相对独立的网络环境 主机能ping虚拟机 虚拟机就好像直接连在互联网上
  • 外部 也就是桥接,装完后相当于电脑局域网上一个机子,需要网桥连接 实际上我还没搞懂
  • 专用 hostonly 只和本机 数据交互

放弃ubuntu 桌面版 安装 ubuntu 服务器版

尝试了很很多次,安装了n遍 又仔细看了下知乎下面的评论 具体研究了下虚拟机下的网络
谷歌看了很多文章 突然看到百度一篇很有用的样子
参考博客

后续:服务器版本 装桌面环境太复杂,c盘空间不够。删了

放弃服务器版 返回桌面版 最终基本成功

经过两三天 n 小时的折腾 卡顿程度稍好于虚拟机,窗口拖动较友好
可能还是比不了真机,但真机实在不适合笔记本,先用着看,明天开始搭环境 web 开发走起
可能适合喜欢用键盘的人 鼠标可能还是不适合 还有输入中文有问题

具体操作参见我另一篇博客

一不小心 Ubuntu 重启后又不能联网了,正好我想把xserver关掉关不掉
,这回直接安装服务器版本

网络

重装完又不行,折腾一两天,这次感觉终于弄懂了

关键步骤

  1. 懂的人都懂 hyper-v 新添加个internal 的网卡,在windows 里面共享一下
  2. 查看 internal 网卡 ip 192.168.137.1
  3. 设置ubuntu IP地址 192.168.137.[2~254] 网关为 192.168.137.1 dns 设置随便找一个靠谱的
  4. 代码如下:
  1. ifconfig 查看网卡信息
  2. vim /etc/etc/netplan/xxxx.yaml 开始编辑网络文件 注意目前只发现ubuntu用了netplan 其他的请自行谷歌啥怎么配网络
  3. sudo netplan apply

踩的坑

  1. 其实桥接和直接用nat都行在这上面废了很大功夫
  2. 主机能ping 虚拟机
  3. 虚拟机不能ping 主机是因为windows防火墙
  4. 虚拟机一直不能联网,一直没有怀疑过dns (配置文件里叫 nameserver 我也没注意到)(老实说一开始我还不知道啥啥相互ping 就以为是 连接问题
  5. 虚拟机ping我阿里云服务器可以 突然意识到

原理

  1. 关于虚拟机两种网络关系,简单来说,就是创造了个邻居还是儿子
  2. hyper-v 虚拟机比较适合linux 服务器 和windows 虚拟Windows性能几乎无损
  3. 插上网线,配好ip等等,应该就可以联网了

git 多人开发

分支的理解

  1. 只有一条时间线

  1. 新建分支只是增加一个指向时间线的指针

  2. 继续更新时间线

  3. 合并 让master的指针指向dev就行了,这时候如果要删除dev分支就只需要删除这个dev指针就行了:

5. 解决冲突

  • 当多个分支同时,就会变成这样:

    这种情况,git无法像刚刚一样,直接将master指针移向feature1那样,如果直接输入合并命令,就会合并失败

  • 合并失败后git会将工作区冲突的文件变成这样:

    <<<<<<< HEAD
    ddd
    =======
    dev
    >>>>>>> dev
    
  • 修改过后再add commit一次 就会合并成功

    就像这样:

6. bug分支 feature分支

多人协作

多人协作是git的重要特性

冲突解决

如果 origin被其他人push过后,自己再想push就会有冲突,必须先pull pull后文件也会像这样:

<<<<<<< HEAD
		System.out.println("dev");
		System.out.println("fdsf");
=======
>>>>>>> d46723cb2f115382b802f7c02f9020beea4714cb

必须解决冲突后提交后再push

打标签

标签就是对那串哈希出来的码取的特殊名字

  1. 给当前版本,当前分支打标签:git tag v1.0

  2. 查看所有标签:git tag

  3. 给指定的commit id 打标签:git tag v0.9 f52c633

  4. 还可以创建带有说明的标签,用-a指定标签名,-m指定说明文字:

    $ git tag -a v0.1 -m "version 0.1 released" 1094adb
    
  5. 用命令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而言,这样必授权其他电脑拥有改写自己整个账号代码的权利,配合GitHub,如果我们想改其他人的代码,可以先fork下来,这样就变成你账号下的项目了,自己随心所欲修改后可以提交pull request,别人如果心情好,可能就采纳l

参考:[廖雪峰git教程](

腾讯云二面

介绍下实习项目 shiro 权限模型
rpc框架介绍
jvm调优(不会)
垃圾回收(忘了)
tcp 网络
算法题一亿条短信查找重复的最多的
我的缺点优点最自豪的事情
反问

wego第一次上课总结

计算机硬件

三大主要硬件

  1. cpu: 大脑 ,计算者,操控者
  2. 内存: 类似于工作台,数据运行展示时候存在的位置
  3. 硬盘: 打开我的电脑能看到的一块块存文件的地方,打开一个文件的本质就是从硬盘里读取到内存里然后展示

软件

软件分为操作系统(window,linux)和应用软件(如qq ,word)
操作系统是最底层的软件,他是应用软件和硬件连接的桥梁

文件

文件的本质是磁盘上的一片数据,一块数据,是以二进制的方式存在硬盘里的,如果这个二进制符合某种编码(比如c语言里的ASCII)那这种文件被称为文本文件,你右键新建一个文本文件就是新建了一个二进制文件,而所有代码本身都是二进制文件的形式存在的(只不过改了后缀名 ,c语言的.c .cpp 网页的.html) 所以很多时候写代码我都会叫你新建一个.txt然后手动改成目标的后缀

应用程序

另一种特殊的文件,符合操作系统的某种规范,所以可以双击直接运行,在windows里一般以.exe后缀,c语言本质就是把文本文件的代码编译成操作系统认的exe 应用程序

打开方式

这个概念上课忘说了,文件只是文件,他自己是不可以打开自己的,任何文件的打开都是由另一个应用程序打开,比如双击.txt的文件是用记事本这个应用程序打开了这个文件,,,而记事本打开文件的规则就是用文本文件的方式编辑他,c编译器打开文件后的效果就是用c语言的语法编译它,浏览器打开文件就是用浏览器的方式渲染它

html

这个东西属于今晚临时想加的,重点就是解释下js这个语言可以用在哪。

我们用html的写了一个按钮,然后让点击这个按钮后可以执行一段js代码。
想学习html可参考网上教程

浏览器 按 f12 后选择控制台编写js代码

这里可以非正式的写一些代码,具体操作参考群文件
借助浏览器入门编程.pdf,用这个的目的只是为了便于演示的编程的基础(变量,函数 , if ,while),并不是直接推荐大家学习js这门语言。

春招面试总结

21年大三双非春招总结

最后结果:拿到网易杭州研究院、蚂蚁金融云offer

腾讯五次面试 挂

阿里

网易

美团

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.