开发日记

开发笔记

2021-08-10

  • rpx和px换算:rpx是微信小程序中css的尺寸单位,可以根据屏幕宽度进行自适配,规定理想屏幕宽度为750px,
    • 若实际屏幕宽度w=750px,则10rpx=10px,
    • 若w=375px,则10rpx=5px;

2021-08-11

  • 获取本机局域网IP地址:ipconfig /all;

2021-09-07

  • git创建本地分支并push到远程:
    (1) 切换到基础分支,如主干
    git checkout master
    (2)创建并切换到新分支
    git checkout -b panda
    git branch可以看到已经在panda分支上
    (3)更新分支代码并提交
    git add *
    git commit -m "init panda"
    git push origin panda
    (4)在git代码管理界面经可以看到panda分支了,成功~~

2021-09-08

  • git remote add origin xxx
    创建远程连接
  • cmd进入D盘指令:C:\Users\horseweed>d:

  • 把一个list全部添加到另一个list中:list1.addAll(list2)

2022-04-25

  • 交换两个变量

    var a = 1
    var b = 2
    a = b.also { b = a }
    
  • if not null

    val value = ……
    
    value?.let {
    …… // 代码会执行到此处, 假如data不为null
    }
    
  • 在可能会空的集合中取第一元素

    val emails = …… // 可能会是空集合
    val mainEmail = emails.firstOrNull() ?: ""
    
  • if null 执行一个语句

    val values = ……
    val email = values["email"] ?: throw IllegalStateException("Email is missing!")
    
  • if-not-null 缩写

    val files = File("Test").listFiles()
    println(files?.size) // 如果 files 不是 null,那么输出其大小(size)
    
  • if-not-null-else 缩写

    val files = File("Test").listFiles()
    
    println(files?.size ?: "empty") // 如果 files 为 null,那么输出“empty”
    
    // To calculate the fallback value in a code block, use `run`
    val filesSize = files?.size ?: run { 
        return someSize 
    }
    println(filesSize)
    
  • 类型检测

    • 使用 is 操作符或其否定形式 !is 在运行时检测对象是否符合给定类型:

      if (obj is String) {
          print(obj.length)
      }
      
      if (obj !is String) { // 与 !(obj is String) 相同
          print("Not a String")
      } else {
          print(obj.length)
      }
      
  • 默认情况下,Kotlin 类是最终(final)的——它们不能被继承。 要使一个类可继承,请用 open 关键字标记它:

  • 如需声明一个显式的超类型,请在类头中把超类型放到冒号之后:

    open class Base(p: Int)
    
    class Derived(p: Int) : Base(p)
    
  • 还可以使用索引范围(minValue..maxValue)或(maxValue..minvalue)遍历Kotlin的数组元素。让无涯教程来看一个使用范围遍历数组的示例。

    fun main(args: Array<String>){
        var myArray5: IntArray = intArrayOf(5,10,20,12,15)
    
        for (index in 0..4){
            println(myArray5[index])
        }
        println()
        for (index in 0..myArray5.size-1){
            println(myArray5[index])
        }
    }
    

2022-04-26

  • Kotlin - ArrayList

    • 创建一个简单的ArrayList类:

      val arrayList = ArrayList<String>()//创建一个空数组列表
      
    • 使用集合填充ArrayList中的元素:

      fun main(args: Array<String>){
      
          val arrayList: ArrayList<String> = ArrayList<String>(5)
          var list: MutableList<String> = mutableListOf<String>()
      
          list.add("Ajay")
          list.add("Vijay")
          list.add("Learnfk")
      
          arrayList.addAll(list)
          println("......print ArrayList......")
          val itr = arrayList.iterator()
          while(itr.hasNext()) {
              println(itr.next())
          }
          println("size of arrayList = "+arrayList.size)
      }
      
    • get() - 用于检索给定指定索引处存在的元素

      println( arrayList.get(2))
      
    • set() - 用于在给定索引处设置给定元素,如果在给定索引处存在任何元素,则将其替换

      arrayList.set(2,"Ashu")
      
    • indexOf() - 用于检索第一次出现的元素的索引值,或者如果列表中不存在指定的元素,则返回-1

      println(arrayList.indexOf("Vijay"))
      
    • lastindexOf() - 用于检索元素的最后一次出现的索引值,或者如果列表中不存在指定的元素,则返回-1

      println(arrayList.lastIndexOf("Vijay"))
      
    • remove() - 用于删除列表中首次出现的元素

      arrayList.remove("Vijay")
      
    • removeAt() - 用于从列表中删除指定索引的元素

      arrayList.removeAt(3)
      
    • clear() - 用于删除(清除)列表的所有元素

      arrayList.clear()
      
  • Kotlin - MutableList

    • Kotlin MutableList是一个接口和元素的通用集合。 MutableList接口本质上是可变的。它继承了Collection 类的形式。 MutableList接口的方法支持读取和写入函数。一旦声明了MutableList中的元素,就可以在其中添加或删除更多元素,因此它没有固定的大小长度。

      要使用MutableList接口,无涯教程使用其称为mutableListOf()或mutableListOf()的函数

    • 	var mutableList = mutableListOf<String>()
      
          mutableList.add("Ajay")//index 0
          mutableList.add("Vijay")//index 1
          mutableList.add("Learnfk")//index 2
      
          var mutableList2 = mutableListOf<String>("Rohan","Raj")
          var mutableList3 = mutableListOf<String>("Dharmesh","Umesh")
          var mutableList4 = mutableListOf<String>("Ajay","Dharmesh","Ashu")
      
      .....mutableList.....
      Ajay
      Vijay
      Learnfk
      .....mutableList[2].....
      Learnfk
      ......mutableList.add(2,"Rohan")......
      Ajay
      Vijay
      Rohan
      Learnfk
      .....mutableList.add("Ashu")......
      Ajay
      Vijay
      Rohan
      Learnfk
      Ashu
      ... mutableList.addAll(1,mutableList3)....
      Ajay
      Dharmesh
      Umesh
      Vijay
      Rohan
      Learnfk
      Ashu
      ...mutableList.addAll(mutableList2)....
      Ajay
      Dharmesh
      Umesh
      Vijay
      Rohan
      Learnfk
      Ashu
      Rohan
      Raj
      ...mutableList.remove("Vijay")....
      Ajay
      Dharmesh
      Umesh
      Rohan
      Learnfk
      Ashu
      Rohan
      Raj
      ....mutableList.removeAt(2)....
      Ajay
      Dharmesh
      Rohan
      Learnfk
      Ashu
      Rohan
      Raj
      ....  mutableList.removeAll(mutableList2)....
      Ajay
      Dharmesh
      Learnfk
      Ashu
      ....mutableList.set(2,"Ashok")....
      Ajay
      Dharmesh
      Ashok
      Ashu
      .... mutableList.retainAll(mutableList4)....
      Ajay
      Dharmesh
      Ashu
      .... mutableList2.clear()....
      .... mutableList2 after mutableList2.clear()....
      []
      ....mutableList.subList(1,2)....
      [Dharmesh]
      
  • ArrayList类的元素也可以使用内置的iterator()函数遍历

        val list: ArrayList<String> = arrayListOf<String>()
    
        list.add("Ajay")
        list.add("Vijay")
        list.add("Learnfk")
    
    	val itr = list.iterator()
        while(itr.hasNext()) {
            println(itr.next())
        }
    
  • kotlin中ArrayList和MutableList的关系:ArrayList实现了MutableList

img
  • Kotlin - Null空类型

    • Kotlin null 安全性是一种从代码中消除空引用风险的程序。如果Kotlin编译器发现在不执行任何其他语句的情况下传递了任何null参数,则会立即引发NullPointerException

    • Kotlin类型系统区分可以包含null(可为空的引用)和不能包含null(非空引用)的引用。通常,String的类型不能为空。要使字符串包含空值,必须通过?来明确定义它们。后面的字符串为:String?

    • val str: String = null//编译错误
      str = "hello"//编译错误 Val 不能被重新分配
      var str2: String = "hello"
      str2 = null//编译错误
      
  • Kotlin - 智能转换

    • 智能强制转换是Kotlin编译器在if表达式内跟踪条件的功能。如果编译器发现变量不是可为null的类型的null,则编译器将允许访问该变量。

    • 在没有安全类型转换的情况下访问可为null的String类型时,它将生成编译错误

      • var string: String? = "Hello!"
        print(string.length)//编译错误
        
    • 为了解决上面的表达式,可使用一个安全的强制转换为:

      • fun main(args: Array<String>){
            var string: String? = "Hello!"
            if(string != null) {//smart cast
               print(string.length)//It works now!
            }
        }
        
    • 当使用is或!is检查变量时,编译器将跟踪此信息并将变量内部转换为目标类型。如果is或!is返回true,则在范围内完成此操作

      • fun main(args: Array<String>){
            val obj: Any = "The variable obj is automatically cast to a String in this scope"
            if(obj is String) {
                //不需要显式转换。
                println("String length is ${obj.length}")
            }
        }
        
      • 输出:String length is 64

      • fun main(args: Array<String>){
            val obj: Any = "The variable obj is automatically cast to a String in this scope"
            if(obj !is String) {
              println("obj is not string")
            } else
            //不需要显式转换。
            println("String length is ${obj.length}")
        }
        
      • 输出:String length is 64

  • Kotlin - 安全转换

    • 有时无法强制转换变量并引发异常,这称为不安全强制转换。不安全的强制转换由infix运算符as进行。

      无法将可为空的字符串(String?)强制转换为非nullabe字符串(String),这将引发异常。

    • fun main(args: Array<String>){
        val obj: Any? = null
        val str: String = obj as String
        println(str)
      }
      
    • 上面的程序抛出异常:

      Exception in thread "main" kotlin.TypeCastException: null cannot be cast to non-null type kotlin.String
       at TestKt.main(Test.kt:3)
      
    • Kotlin提供了一个安全转换 as? 安全地转换为类型。如果无法强制转换,则返回null,而不是引发ClassCastException异常。

      fun main(args: Array<String>){
        val location: Any = "Kotlin"
        val safeString: String? = location as? String
        val safeInt: Int? = location as? Int
        println(safeString)
        println(safeInt)
      }
      
    • 输出:

      Kotlin
      null
      
  • Kotlin - Elvis运算符

    • Elvis运算符(?:)用于返回非null值,即使条件表达式为null。它还用于检查值的空安全性。

      在某些情况下,可以声明一个可以包含空引用的变量。假设一个包含空引用的变量str,在程序中使用str之前,将检查它的可空性。如果发现变量str不为null,则其属性将使用,否则将使用其他一些非null值。

    • fun main(args: Array<String>){
      
      var str: String? = null
      var str2: String? = "May be declare nullable string"
      var len1:  Int = if (str != null) str.length else -1
      var len2:  Int = if (str2 != null) str2.length else -1
      println("Length of str is ${len1}")
      println("Length of str2 is ${len2}")
      }
      
    • Kotlin提供了Elvis运算符(?:)的高级运算符,即使条件表达式为null,该运算符也会返回非null值。上面的if...else运算符可以使用Elvis运算符表示如下:

    • var len1:  Int = str?.length ?: -1
      var len2:  Int = str2?.length ?:  -1
      
  • Kotlin - 函数参数

    • 如果在不传递任何参数的情况下调用函数,则默认参数将用作函数定义的参数。当使用实参调用函数时,传递的实参将用作函数定义中的参数。

    • fun main(args: Array<String>) {
          run()
      }
      fun run(num:Int= 5, latter: Char ='x') {
          print("parameter in function definition $num and $latter")
      }
      
    • 输出:parameter in function definition 5 and x

  • Kotlin - 范围函数

    • rangeTo():

      • 用于以给定step值的间隔返回范围值用于按范围中提到的升序从头到尾返回值。 rangeTo函数是整数类型,它调用Range类的构造函数

      • fun main(args: Array<String>) {
        	var range: IntRange = 1.rangeTo(5)
            var range2: IntRange = IntRange(1,5)
            for (x in range)	print("$x ")
            for (x in range2)	print("$x ")
        }
        
    • downTo()

      • 用于按递减顺序将值从高阶返回到低阶。 downTo函数是为一对整数类型定义的

      • var range1 = 5 downTo 1
        var range2: IntProgression = 5.downTo(1)
        var range3: IntProgression = IntProgression.fromClosedRange(5,1,-1)
        
    • reversed()

      • 用于返回给定范围类型的逆序

      • var range1 = 1..5
        for (x in range1.reversed()){
        	print("$x ")
        }
        
    • step()

      • 用于以给定step值的间隔返回范围值

      • val range: IntRange = 1..10
        for(x in range step (2)){
        	print("$x ")
        }
        
        //1 3 5 7 9 
        
  • 接口 - 接口声明

    interface MyInterface {
        val id: Int//抽象属性
        fun absMethod()// 抽象方法
        fun doSomthing() {
         //可选方法体
        }
    }
    
  • 接口 - 接口实例

    interface MyInterface  {
        var id: Int           //抽象属性
        fun absMethod():String   //抽象方法
        fun doSomthing() {
          println("MyInterface doing some work")
        }
    }
    class InterfaceImp : MyInterface {
        override var id: Int = 101
        override fun absMethod(): String{
          return "Implementing abstract method.."
        }
    }
    fun main(args: Array<String>) {
      val obj = InterfaceImp()
      println("Calling overriding id value = ${obj.id}")
      obj.doSomthing()
      println(obj.absMethod())
    }
    
  • 接口 - 实现多个接口

    interface MyInterface1 {
        fun doSomthing()
    }
    interface MyInterface2 {
        fun absMethod()
    }
    class MyClass : MyInterface1, MyInterface2 {
        override fun doSomthing() {
          println("overriding doSomthing() of MyInterface1")
        }
    
        override fun absMethod() {
          println("overriding absMethod() of MyInterface2")
        }
    }
    fun main(args: Array<String>) {
      val myClass = MyClass()
      myClass.doSomthing()
      myClass.absMethod()
    }
    
  • 接口 - 实现多个接口(包含名称相同的抽象方法)

    interface MyInterface1 {
        fun doSomthing() {
          println("MyInterface 1 doing some work")
        }
        fun absMethod()
    }
    interface MyInterface2 {
        fun doSomthing(){
          println("MyInterface 2 doing some work")
        }
        fun absMethod(name: String)
    }
    class MyClass : MyInterface1, MyInterface2 {
        override fun doSomthing() {
            super<MyInterface2>.doSomthing() 
            // 需要使用超级关键字super <interface_name> .methodName()指定接口名称
        }
    
        override fun absMethod() {
          println("Implements absMethod() of MyInterface1")
        }
        override fun absMethod(n: String) {
          println("Implements absMethod(name) of MyInterface2 name is  $n")
        }
    }
    fun main(args: Array<String>) {
      val myClass = MyClass()
      myClass.doSomthing()
      myClass.absMethod()
      myClass.absMethod("Ashu")
    }
    
    // 输出
    MyInterface 2 doing some work
    Implements absMethod() of MyInterface1
    Implements absMethod(name) of MyInterface2 name is  Ashu
    

2022-04-27

  • 今天遇到一个问题,kotlin中无法调取java类中private修饰的字段,随后发现是lombok的@Data无法被kotlin读取,只能手写setter和getter

  • Mock:在测试过程中,对于某些不容易构造或者不容易获取的对象,用一个虚拟的对象来创建以便测试

  • kotlin - lateinit:在Kotlin中定义变量或者属性都是需要初始化值的,并且其都是private的,但是有些时候对于变量或者属性只需要声明,但是不需要初始化,则kotlin提供了lateinit关键字来实现

    • lateinit 是一个坑,对于萌新来说还是一个不小的坑。

      当我们用 lateinit 修饰类属性的时候,实际上在告诉编译器:这个属性的初始化的时机和方式由我亲自把控,你个垃圾编译器哪儿凉快哪儿待着去。

    • 但可爱的 Kotlin 编译器不能坐视你把 null 赋给非空的 String 类型呀,很贴心地把这个类里所有访问 lateinit 属性的地方都做了一次空检查:

      // 字节码等价代码
      fun printValue() {
        val tempValue = this.value
        if(tempValue == null) {
          throw UninitializedPropertyAccessException("lateinit property value has not been initialized")
        }
        println(tempValue)
      }
      

      没错,你定义的 value 属性确实是 null,但因为你给 Kotlin 的类型是 String,不能容纳 null,Kotlin 为了保证“绝对的空安全”,只能抛出异常了事。这个锅是谁的?编译器当然一脸无辜:肯定是你的,谁让你用 lateinit 呢?年轻人呐,自由是有代价的,你用 lateinit 获得了片刻的自由,得到的却是无尽的异常;

    • 回到正题,lateinit 的蛋疼之处在于,萌新以为找到了接近 Java 的写法,但实际上一只脚已经踩在了地雷上;有一定 Kotlin 经验的同学呢,要么觉得 lateinit 给的自由度完全不够,必要的时候直接 “var + 可空类型”浪起,要么处处担惊受怕,生怕碰到没有初始化的属性。这样,lateinit 就变成一个十足的鸡肋了,会用的不想用,不会用的处处掉坑

2022-04-28

  • domain:domain的概念,通常会分很多层,比如经典的三层架构,控制层、业务层、数据访问层(DAO),此外,还有一个层,就是domain层
    • domain层,通常就是用于放置这个系统中,与数据库中的表,一一对应起来的JavaBean的
    • model层:和domain区别;可能都是javaBean,这个区别是用途不同,domain通常就代表了与数据库表一一对应的javaBean,model有可能是VO

2022-04-29

  • 深拷贝:

    • BeanUtils.copyProperties(newObject, oldObject)
      
  • arrayList.removeIf(Predicate filter)

    • studentList.removeIf(s -> s.getAddress().equals("淄博"));
      
    • 如果元素被删除则返回 true;

  • /**
     * 根据工作空间日志查找对应文档
     * @param workspaceLogs 工作空间日志列表
     */
    private fun findWorkspaceMemberTagByLogs(workspaceLogs: List<WorkspaceLog>): List<WorkspaceMemberTag> {
        val workspaceIds = workspaceLogs.map { it.workspaceId }
        val query = Query(Criteria("workspaceId").`in`(workspaceIds))
        return fromMongoTemplate.find(query, WorkspaceMemberTag::class.java, WorkspaceMemberTag::class.java.simpleName)
    }
    
  • filterStudents.forEach(filterStudent -> studentList.remove(filterStudent));
    
    // 等价于
    
    filterStudents.forEach(studentList::remove);
    

2022-05-05

  • 对于固定大小的List,如:

          List<String> strings = Arrays.asList("abc", "", "bc", "efg", "abcd","", "jkl");
    

    不可以对其进行进行增删操作,否则会抛出UnsupportedOperationException;

  • 中间人攻击
    SSH之所以能够保证安全,原因在于它采用了公钥加密。
    整个过程是这样的:

    (1)远程主机收到用户的登录请求,把自己的公钥发给用户。

    (2)用户使用这个公钥,将登录密码加密后,发送回来。

    (3)远程主机用自己的私钥,解密登录密码,如果密码正确,就同意用户登录。

    这个过程本身是安全的,

    但是实施的时候存在一个风险:如果有人截获了登录请求,然后冒充远程主机,将伪造的公钥发给用户,那么用户很难辨别真伪。因为不像https协议,SSH协议的公钥是没有证书中心(CA)公证的,也就是说,都是自己签发的。

2022-05-06

大江哥的胡乱代码

select t1.*,t9.TypeName from b_company t1
left join b_typeentity t2 on t1.level = t2.TypeCode
left join b_typeentity t3 on t1.LegalPerson = t3.TypeCode
left join b_typeentity t4 on t1.RegID = t4.TypeCode
left join b_typeentity t6 on t1.CustomType = t6.TypeCode
left join b_typeentity t7 on t1.BBCompanyName = t7.TypeCode
left join b_typeentity t8 on t1.Authentic = t8.TypeCode
left join mysql.help_topic b ON b.help_topic_id < ( length( t1.Industry ) - length( REPLACE ( t1.Industry, ',', '' ) ) + 1 )
left join b_typeentity t9 on t9.TypeCode in (substring_index( substring_index( t1.Industry, ',', b.help_topic_id + 1 ), ',',- 1 ) )

  • 跨域请求:

简单来说,当一台服务器资源从另一台服务器(不同 的域名或者端口)请求一个资源或者接口,就会发起一个跨域 HTTP 请求。举个简单的例子,从http://aaa.com/index.html, 发送一个 Ajax 请求,请求地址是 http://bbb.com/ 下面的一个接口,这就是发起了一个跨域请求。在不做任何处理的情况下,这个跨域请求是无法被成功请求的,因为浏览器基于同源策略会对跨域请求做一定的限制。

  • 同源策略:域名请求地址的组成是:协议+域名+端口号+请求资源地址 , 当协议、域名、端口号中任意一个不相同时 , 都算作不同源

同源策略发生的场景——浏览器中。如果不是浏览器的话, 就不会受到同源策略的影响。也就是说,两个服务器直接进行跨域请求是可以进行数据请求的。这也就为我们接下来的后端跨域埋下一下小伏笔。 同源策略的目的是什么呢?同源策略限制了从同一个源加载的文档或者脚本如何与来自另 一个源的资源进行交互。这是一个用于隔离潜在恶意文件的重要安全机制

跨域并不是请求发不出去,请求能发出去,服务端能收到请求并正常返回结果,只是结果被浏览器拦截了。你可能会疑问明明通过表单的方式可以发起跨域请求,为什么 Ajax 就不会?因为归根结底,跨域是为了阻止用户读取到另一个域名下的内容,Ajax 可以获取响应,浏览器认为这不安全,所以拦截了响应。但是表单并不会获取新的内容,所以可以发起跨域请求。同时也说明了跨域并不能完全阻止 CSRF,因为请求毕竟是发出去了。
————————————————
原文链接:https://blog.csdn.net/weixin_40910372/article/details/100068498

  • 后端解决方案:

    @Configuration
    public class CorsConfig {
        private CorsConfiguration buildConfig() {
            CorsConfiguration corsConfiguration = new CorsConfiguration();
            corsConfiguration.setAllowCredentials(true);
            corsConfiguration.addAllowedOrigin("*"); //允许任何域名
            corsConfiguration.addAllowedHeader("*"); //允许任何头
            corsConfiguration.addAllowedMethod("*"); //允许任何方法
            return corsConfiguration;
        }
    
        @Bean
        public CorsFilter corsFilter(HttpServletRequest request) {
            UrlBasedCorsConfigurationSource source = new UrlBasedCorsConfigurationSource();
            source.registerCorsConfiguration("/**", buildConfig()); //注册
            return new CorsFilter(source);
        }
    }
    
  • Content-Type(内容类型):一般是指网页中存在的 Content-Type,用于定义网络文件的类型和网页的编码,决定浏览器将以什么形式、什么编码读取这个文件;如:application/json

    • 常见的媒体格式类型如下:
      • text/html : HTML格式
      • text/plain :纯文本格式
      • text/xml : XML格式
      • image/gif :gif图片格式
      • image/jpeg :jpg图片格式
      • image/png:png图片格式
    • 以application开头的媒体格式类型:
      • application/xhtml+xml :XHTML格式
      • application/xml: XML数据格式
      • application/atom+xml :Atom XML聚合格式
      • application/json: JSON数据格式
      • application/pdf:pdf格式
      • application/msword : Word文档格式
      • application/octet-stream : 二进制流数据(如常见的文件下载)
      • application/x-www-form-urlencoded :
        中默认的encType,form表单数据被编码为key/value格式发送到服务器(表单默认的提交数据的格式)
    • 另外一种常见的媒体格式是上传文件之时使用的:
      • multipart/form-data : 需要在表单中进行文件上传时,就需要使用该格式
  • mapper中的namespace用于绑定dao接口的,即面向接口编程

    image-20220506144028404

  • Nacos(Dynamic Naming and Configuration Service):

  • 拦截器:实现WebMvcConfigurer接口,重写addInterceptors方法,同时标注@Configuration

    @Configuration
    public class AuthConfig implements WebMvcConfigurer {
        @Autowired
        private CacheServiceRedis cacheServiceRedis;
    
        @Override
        public void addInterceptors(InterceptorRegistry registry) {
            registry.addInterceptor(new LoginInterceptor(cacheServiceRedis))
                    .addPathPatterns("/**")
                    .excludePathPatterns("/user/login");
        }
    }
    
  • LoginInterceptor实现HandlerInterceptorAdapter类,重写preHandle方法

    public class LoginInterceptor extends HandlerInterceptorAdapter {
        private CacheServiceRedis cacheServiceRedis;
        public LoginInterceptor(CacheServiceRedis cacheServiceRedis) {
            this.cacheServiceRedis = cacheServiceRedis;
        }
    
        @Override
        public boolean preHandle(HttpServletRequest request,
                                 HttpServletResponse response, Object object) throws Exception {
            String bearToken = request.getHeader(SampleConstants.GAP_TOKNE);
            if (StringUtil.isEmpty(bearToken)) {
                ErrorUtil.writeErrInfo(response);
                return false;
            }
            // 不空时,验证token是否合法
            UserCacheModel userCacheModel = cacheServiceRedis.get(CacheKeyPrefix.TOKEN_KEY_PREFIX,
                    bearToken,UserCacheModel.class);
            TbUser user = userCacheModel.getUser();
            if(! HashUtil.md5(user.toString() + bearToken)
                    .equalsIgnoreCase(userCacheModel.getHashValue())) {
                ErrorUtil.writeErrInfo(response);
                return false;
            }
            return true;
        }
    }
    

2022-05-07

  • Typora下载:Typora(morkdown编辑器)

  • 服务注册:服务进程在注册中心注册自己的元数据信息。通常包括主机和端口号,有时还有身份验证信息,协议,版本号,以及运行环境的信息。

  • 服务发现:客户端服务进程向注册中心发起查询,来获取服务的信息。服务发现的一个重要作用就是提供给客户端一个可用的服务列表。

  • 单体架构:微服务架构出现之前业界最经典的软件架构类型。单体架构将应用程序中所有业务逻辑都编写在同一个工程中,最终经过编译、打包,部署在一台服务器上运行;

  • 微服务:一种系统架构的设计风格

    与传统的单体式架构(ALL IN ONE)不同,微服务架构提倡将一个单一的应用程序拆分成多个小型服务,这些小型服务都在各自独立的进程中运行,服务之间使用轻量级通信机制(通常是 HTTP RESTFUL API)进行通讯;

    通常情况下,这些小型服务都是围绕着某个特定的业务进行构建的,每一个服务只专注于完成一项任务并把它做好;

    每个服务都能够独立地部署到各种环境中,例如开发环境、测试环境和生产环境等,每个服务都能独立启动或销毁而不会对其他服务造成影响;

    服务之间的交互是使用标准的通讯技术进行的,因此不同的服务可以使用不同数据存储技术,甚至使用不同的编程语言;

  • 随着微服务的火爆流行,国内外各大互联网公司都相继分享了他们在微服务架构中,针对不同场景出现的各种问题的解决方案和开源框架。

    • 服务治理:阿里巴巴开源的 Dubbo 和当当网在其基础上扩展出来的 DubboX、Netflix 的 Eureka 以及 Apache 的 Consul 等;
    • 分布式配置管理:百度的 Disconf、Netflix 的 Archaius、360 的 QConf、携程的 Apollo 以及 Spring Cloud 的 Config 等;
    • 批量任务:当当网的 Elastic-Job、LinkedIn 的 Azkaban 以及 Spring Cloud 的 Task 等;
    • 服务跟踪:京东的 Hydra、Spring Cloud 的 Sleuth 以及 Twitter 的 Zipkin 等;
    • 以上这些微服务框架或解决方案都具有以下 2 个特点:
      • 对于同一个微服务问题,各互联网公司给出的解决方案各不相同。
      • 一个微服务框架或解决方案都只能解决微服务中的某一个或某几个问题,对于其他问题则无能为力
  • Spring Cloud 并不是一个拿来即可用的框架,它是一种微服务规范,共有以下 2 代实现:

    • 第一代实现:Spring Cloud Netflix
    • 第二代实现:Spring Cloud Alibaba

    Spring Cloud 被称为构建分布式微服务系统的“全家桶”,它并不是某一门技术,而是一系列微服务解决方案或框架的有序集合。它将市面上成熟的、经过验证的微服务框架整合起来,并通过 Spring Boot 的思想进行再封装,屏蔽调其中复杂的配置和实现原理,最终为开发人员提供了一套简单易懂、易部署和易维护的分布式系统开发工具包。

    Spring Cloud 中包含了 spring-cloud-config、spring-cloud-bus 等近 20 个子项目,提供了服务治理、服务网关、智能路由、负载均衡、断路器、监控跟踪、分布式消息队列、配置管理等领域的解决方

  • Spring Boot 和 Spring Cloud 的区别与联系:

    • Spring Boot 和 Spring Cloud 分工不5同

    Spring Boot 是一个基于 Spring 的快速开发框架,它能够帮助开发者迅速搭 Web 工程。在微服务开发中,Spring Boot 专注于快速、方便地开发单个微服务。

    Spring Cloud 是微服务架构下的一站式解决方案。Spring Cloud 专注于全局微服务的协调和治理工作。换句话说,Spring Cloud 相当于微服务的大管家,负责将 Spring Boot 开发的一个个微服务管理起来,并为它们提供配置管理、服务发现、断路器、路由、微代理、事件总线、决策竞选以及分布式会话等服务。

    • Spring Cloud 是基于 Spring Boot 实现的

    Spring Cloud 是基于 Spring Boot 实现的。与 Spring Boot 类似,Spring Cloud 也为提供了一系列 Starter,这些 Starter 是 Spring Cloud 使用 Spring Boot 思想对各个微服务框架进行再封装的产物。它们屏蔽了这些微服务框架中复杂的配置和实现原理,使开发人员能够快速、方便地使用 Spring Cloud 搭建一套分布式微服务系统。

    • Spring Boot 和 Spring Cloud 依赖项数量不同

    Spring Boot 属于一种轻量级的框架,构建 Spring Boot 工程所需的依赖较少。

    Spring Cloud 是一系列微服务框架技术的集合体,它的每个组件都需要一个独立的依赖项(Starter POM),因此想要构建一套完整的 Spring Cloud 工程往往需要大量的依赖项。

    • Spring Cloud 不能脱离 Spring Boot 单独运行

    Spring Boot 不需要 Spring Cloud,就能直接创建可独立运行的工程或模块。

    Spring Cloud 是基于 Spring Boot 实现的,它不能独立创建工程或模块,更不能脱离 Spring Boot 独立运行。

    注意:虽然 Spring Boot 能够用于开发单个微服务,但它并不具备管理和协调微服务的能力,因此它只能算是一个微服务快速开发框架,而非微服务框架。

  • Spring Cloud Eureka:Eureka 是 Spring Cloud Netflix 模块的子模块,它是 Spring Cloud 对 Netflix Eureka 的二次封装,主要负责 Spring Cloud 的服务注册与发现功能;

  • Eureka 采用 CS(Client/Server,客户端/服务器) 架构,它包括以下两大组件:

    • Eureka Server:Eureka 服务注册中心,主要用于提供服务注册功能。当微服务启动时,会将自己的服务注册到 Eureka Server。Eureka Server 维护了一个可用服务列表,存储了所有注册到 Eureka Server 的可用服务的信息,这些可用服务可以在 Eureka Server 的管理界面中直观看到。
    • Eureka Client:Eureka 客户端,通常指的是微服务系统中各个微服务,主要用于和 Eureka Server 进行交互。在微服务应用启动后,Eureka Client 会向 Eureka Server 发送心跳(默认周期为 30 秒)。若 Eureka Server 在多个心跳周期内没有接收到某个 Eureka Client 的心跳,Eureka Server 将它从可用服务列表中移除(默认 90 秒)。

    注:“心跳”指的是一段定时发送的自定义信息,让对方知道自己“存活”,以确保连接的有效性。大部分 CS 架构的应用程序都采用了心跳机制,服务端和客户端都可以发心跳。通常情况下是客户端向服务器端发送心跳包,服务端用于判断客户端是否在线。

  • Eureka 实现服务注册与发现的原理,如下图所示

    Eureka 服务注册与发现

    上图中共涉及到以下 3 个角色:

    • 服务注册中心(Register Service):它是一个 Eureka Server,用于提供服务注册和发现功能。
    • 服务提供者(Provider Service):它是一个 Eureka Client,用于提供服务。它将自己提供的服务注册到服务注册中心,以供服务消费者发现。
    • 服务消费者(Consumer Service):它是一个 Eureka Client,用于消费服务。它可以从服务注册中心获取服务列表,调用所需的服务。
  • Eureka 实现服务注册与发现的流程如下:

    1. 搭建一个 Eureka Server 作为服务注册中心;
    2. 服务提供者 Eureka Client 启动时,会把当前服务器的信息以服务名(spring.application.name)的方式注册到服务注册中心;
    3. 服务消费者 Eureka Client 启动时,也会向服务注册中心注册;
    4. 服务消费者还会获取一份可用服务列表,该列表中包含了所有注册到服务注册中心的服务信息(包括服务提供者和自身的信息);
    5. 在获得了可用服务列表后,服务消费者通过 HTTP 或消息中间件远程调用服务提供者提供的服务。
  • Problems with using dates in the range October 5, 1582, through October 14, 1582

    The Java java.util.Date and java.util.Timestamp classes use the Julian calendar for dates before October 4, 1582, and the Gregorian calendar for dates starting with October 4, 1582. In the Gregorian calendar, October 4, 1582, is followed by October 15, 1582. If a Java program encounters a java.util.Date or java.util.Timestamp value that is between October 5, 1582, and October 14, 1582, inclusive, Java adds 10 days to that date. Therefore, a DATE or TIMESTAMP value in a Db2® table that has a value between October 5, 1582, and October 14, 1582, inclusive, is retrieved in a Java program as a java.util.Date or java.util.Timestamp value between October 15, 1582, and October 24, 1582, inclusive. A java.util.Date or java.util.Timestamp value in a Java program that is between October 5, 1582, and October 14, 1582, inclusive, is stored in a Db2 table as a DATE or TIMESTAMP value between October 15, 1582, and October 24, 1582, inclusive.

  • java.util.Date java.util.Timestamp 1582-10-04Julian calendar 计时转为Gregorian calendar,同时将1582-10-051582-10-15设为同一天
    
  • Kotlin-Object:用object 修饰的类为静态类,里面的方法和变量都为静态的。

2022-05-13

  • IDEA 项目中 target 目录的作用 target 是 idea 默认的编译路径,用来存放项目的:文件和目录、jar包、war包、class文件等。
  • .idea文件夹:.idea存放项目的配置信息,包括历史记录,版本控制信息等。
  • .iml文件:idea 对module 配置信息之意, infomation of module
    iml是 intellij idea的工程配置文件,里面是当前project的一些配置信息。
  • deploy:部署
  • mysql数据库字段设置not null 为什么还能插入空值:not null != 空
  • 事务:
    • 事务的作用是为了保证用户的每一个操作都是可靠的,事务中的每一步操作都必须成功执行,只要有发生异常就回退到事务开始未进行操作的状态。
    • 我们在开发企业应用时,对于业务人员的一个操作实际是对数据读写的多步操作的结合。由于数据操作在顺序执行的过程中,任何一步操作都有可能发生异常,异常会导致后续操作无法完成,此时由于业务逻辑并未正确的完成,之前成功操作数据的并不可靠,需要在这种情况下进行回退。

2022-05-16

  • springboot项目配置文件属性变量引用方式${}和@@用法与区别

    • 以下内容存疑,亲测.application.properties引入/local.properties这些配置文件里的属性也是需要用${}来导入的,目前实际项目和自身测试,@@主要用来引入pom中的属性的

    • ${}常用于pom.xml、环境变量和 application.properties等默认配置文件的属性变量引用。

    • 语法为:field_name=$

    • 示例:

      <properties>
          <dubbo.version>2.7.0</dubbo.version>
      </properties>
       
       <dependencies>
           <dependency>
               <groupId>org.apache.dubbo</groupId>
                <artifactId>dubbo</artifactId>
                <version>${dubbo.version}</version>
           </dependency>
       </dependencies>
      
      #logback日志配置
      log.config.address=classpath:config/logback-spring.xml
      logging.config=${log.config.address}
      
    • @@方式常用于引用springboot非默认配置文件(即其他配置文件)中的变量,是springboot为替代${}属性占位符产生,原因是${}会被maven处理,所以引用非默认配置文件时起不到引用变量的作用

    • 语法:field_name=@field_value@

    • 示例:

    • 在实际项目开发中,为了在不同环境进行测试,我们会在src/main/resources目录下创建config文件夹,并在config中创建多个properties文件,例如:local.properties, development.properties, production.properties,当我们在src/main/resources/application.properties文件中引用src/main/resources/config/local.properties的属性变量时,就要使用@@方式

      #端口配置
      server.port=@server.port.web@
       
      #logback日志配置
      logging.config=@logging.config@
      

2022-05-17

  • Java的新旧时间API

    • Java8新时间

              //直接获取当前时间
              LocalDate localDate = LocalDate.now();
              LocalTime date = LocalTime.now();
              LocalDateTime dateTime = LocalDateTime.now();
      
              //自定义时间
              LocalDate localDate1 = LocalDate.of(2000,4,3);
              LocalTime date1 = LocalTime.of(16,30);
              LocalDateTime dateTime1 = LocalDateTime.of(2000,4,3,16,30);
      
              //日期格式化
              DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
              System.out.println(dateTimeFormatter.format(dateTime));
      
              //调用一些特有的新封装方法
              System.out.println(dateTime1.minusWeeks(3));
              System.out.println(dateTime1.getDayOfMonth());
              System.out.println(dateTime1.plusDays(10));
              /*还有许多方法,类似于增加或减少年月日小时分钟,还可以设置减增几周时间,返回月末星期几等等
              就不一一尝试了,使用的时候直接看源码调用就行*/
      
      
    • 旧时间API

              // 当前时间
              Date d1 = new Date();
              System.out.println("当前时间:");
              System.out.println(d1);
              System.out.println();
              // 起始时间
              Date d2 = new Date(0);
              System.out.println("从1970年1月1日 早上8点0分0秒 开始");
              System.out.println(d2);
              // 从1970年1月1日 早上8点0分0秒 开始经历的毫秒数
              Date d3 = new Date(5000);
              System.out.println("从1970年1月1日 早上8点0分0秒 开始经历了5秒的时间");
              System.out.println(d3);
              // 标准格式化
              SimpleDateFormat sdf =new SimpleDateFormat("yyyy-MM-dd HH:mm:ss " );
              String str = sdf.format(d1);
              System.out.println(str);
      
      
  • 列表判空:CollectionUtils.isEmpty(list)

  • URI:统一资源标识符(Uniform Resource Identifier,URI)

    • 在电脑术语中,统一资源标识符是一个用于标识某一互联网资源名称的字符串。 该种标识允许用户对任何(包括本地和互联网)资源通过特定的协议进行交互操作。URI由包括确定语法和相关协议的方案所定义。
  • URL:统一资源定位符 (Uniform Resource Locator, URL)

    • 统一资源定位符,又叫做网页地址,是互联网上标准的资源的地址(Address)。互联网上的每个文件都有一个唯一的URL,它包含的信息指出文件的位置以及浏览器应该怎么处理它。
  • takeIf

    • 是从 T 对象本身调用,即 T.takeIf;

      // Original code
      if (someObject != null && status) {
         doThis()
      }
      
      // Improved code
      someObject?.takeIf{ status }?.apply{ doThis() }
      
    • predicate 函数将 T 对象作为参数;

    • 根据 predicate 的结果最终返回 this 或 null。

      • 由于当条件为 true 时,最终结果返回的是 this,因此可以进行链式操作:

      • val index 
           = input.indexOf(keyword).takeIf { it >= 0 } ?: error("Error")
        val outFile 
           = File(outputDir.path).takeIf { it.exists() } ?: return false
        
  • MongoDB 中的记录是一个文档,它是由字段和值对组成的数据结构。 MongoDB 文档类似于 JSON 对象。字段的值可以包括其他文档,数组和文档数组。

  • Kotlin集合操作:

    • 集合拷贝:toList()、toMutableList()、toSet()
    • 集合转换:map()、zip()、associate()、flatten()、flatMap()
    • 集合过滤:filter()、filterTo()
    • 集合遍历:rangeTo、until、downTo、step、forEach、forEachIndexed
    • 集合加减
    • 集合分组:groupBy()、groupingBy()
    • 取集合的部分:slice()、take()、drop()、chunked()、windowed()、zipWithNext()
    • 集合排序:Comparable、Comparator、sortedWith、sortedBy、reversed、asReversed、shuffled
    • 集合聚合操作:maxOrNull()、minOrNull()、average()、sum()、sumBy()、reduce()、fold()
  • apply函数
    apply函数扩展了所有的泛型对象,在闭包范围内可以任意调用该对象的任意方法,并在最后返回该对象.

    主要的作用:是可以用来简化初始化对象的功能。
    特别需要注意的是apply函数中表示对象本身使用的是this关键字而不是it。

    img

  • Kotlin:内置函数let、also、with、run、apply

    • Let:

      • 使用方法:

        // 作用1:使用it替代object对象去访问其公有的属性 & 方法
        object.let{
           it.todo()
        }
        
        // 作用2:判断object为null的操作
        object?.let{//表示object不为null的条件下,才会去执行let函数体
           it.todo()
        }
        
        // 注:返回值 = 最后一行 / return的表达式
        
      • 示例:

        // 使用Java
        if( mVar != null ){
            mVar.function1();
            mVar.function2();
            mVar.function3();
        }
        
        // 使用kotlin(无使用let函数)
        mVar?.function1()
        mVar?.function2()
        mVar?.function3()
        
        // 使用kotlin(使用let函数)
        // 方便了统一判空的处理 & 确定了mVar变量的作用域
        mVar?.let {
            it.function1()
            it.function2()
            it.function3()
        }
        
    • also:类似let函数,但区别在于返回值:

      let函数:返回值 = 最后一行 / return的表达式

      also函数:返回值 = 传入的对象的本身

      • 示例:

        // let函数
        var result = mVar.let {
            it.function1()
            it.function2()
            it.function3()
            999
        }
        // 最终结果 = 返回999给变量result
        
        // also函数
        var result = mVar.also {
            it.function1()
            it.function2()
            it.function3()
            999
        }
        // 最终结果 = 返回一个mVar对象给变量result
        
    • With:调用同一个对象的多个方法 / 属性时,可以省去对象名重复,直接调用方法名 / 属性即可

      • 使用方法:

         with(object){
           // ... 
         }
        
        // 返回值 = 函数块的最后一行 / return表达式
        
      • 示例:

        // 此处要调用people的name 和 age属性
        // kotlin
        val people = People("carson", 25)
        with(people) {
        	println("my name is $name, I am $age years old")
        }
        
        // Java
        User peole = new People("carson", 25);
        String var1 = "my name is " + peole.name + ", I am " + peole.age + " years old";
        System.out.println(var1);
        
    • run:结合了let、with两个函数的作用,

      调用同一个对象的多个方法 / 属性时,可以省去对象名重复,直接调用方法名 / 属性即可

      定义一个变量在特定作用域内

      统一做判空处理

      • 使用方法

        object.run{
        // ... 
        }
        // 返回值 = 函数块的最后一行 / return表达式
        
      • 示例:

        // 此处要调用people的name 和 age属性,且要判空
        // kotlin
        val people = People("carson", 25)
        people?.run{
            println("my name is $name, I am $age years old")
        }
        
        // Java
        User peole = new People("carson", 25);
        String var1 = "my name is " + peole.name + ", I am " + peole.age + " years old";
        System.out.println(var1);
        
    • apply:与run函数类似,但区别在于返回值:

      run函数返回最后一行的值 / 表达式

      apply函数返回传入的对象的本身,特别需要注意的是apply函数中表示对象本身使用的是this关键字而不是it

      • 应用场景:对象实例初始化时需要对对象中的属性进行赋值 & 返回该对象

      • 示例:

        // run函数
        val people = People("carson", 25)
        val result = people?.run{
            println("my name is $name, I am $age years old")
            999
        }
        // 最终结果 = 返回999给变量result
        
        // Java
        val people = People("carson", 25)
        val result = people?.apply{
            println("my name is $name, I am $age years old")
            999
        }
        // 最终结果 = 返回一个people对象给变量result
        

img

2022-05-18

  • mongoTemplate:

    • 实现:
      • 常用方法:
    mongoTemplate.findAll(Student.class): 查询Student文档的全部数据
    mongoTemplate.findById(<id>, Student.class): 查询Student文档id为id的数据
    mongoTemplate.find(query(criteria), Student.class);: 根据query内的查询条件查询
    mongoTemplate.upsert(query(criteria), update, Student.class): 修改
    mongoTemplate.remove(query(criteria), Student.class): 删除
    mongoTemplate.insert(student): 新增
    
    mongoTemplate.find(
        Query(
            Criteria.where("systemFieldList").elemMatch(
                Criteria.where(FieldConst.CODE).`is`(FieldConst.ORG_TYPE_NAME).and("values.value")
                .regex(keywords)
            )
        ), OrgTypeConfig::class.java
    )
    
  • Kotlin正则匹配:直接DATE_PATTERN_REGEX.matches(time)

  • thread类中start()和run()方法的区别:

    调用start方法方可启动线程,而run方法只是thread的一个普通方法调用,还是在主线程里执行,要等待run方法体执行完毕后才可继续执行下面的代码: 而如果直接用run方法,这只是调用一个方法而已,程序中依然只有主线程–这一个线程,其程序执行路径还是只有一条,这样就没有达到写线程的目的

2022-05-19

  • asList() 和 toList()

    在这里插入图片描述

  • kotlin集合/数组相关:

    Set常用API Set的默认实现 - LinkedHashSet(保留元素插入的顺序)

    Map常用API,默认实现 – LinkedHashMap:迭代 Map 时保留元素插入的顺序

    不可变List,List 的默认实现是 ArrayList

    1. arrayOf()

      var myArray1 = arrayOf(1,10,4,6,15)
      var myArray2 = arrayOf<Int>(1,10,4,6,15)
      val myArray3 = arrayOf<String>("Ajay","Prakesh","Michel","Learnfk","Sumit")
      var myArray4= arrayOf(1,10,4, "Ajay","Prakesh")
      var myArray5: IntArray = intArrayOf(5,10,20,12,15)
      
    2. mapof()

      val numMap = mapOf("key1" to 1, "key2" to 2, "key3" to 3)
      println(numMap.keys) //[key1, key2, key3]
      println(numMap.values) //[1, 2, 3]  注意:在.values中调用remove()仅删除给定值匹配到的的第一个条目。
      println(numMap.entries) //[key1=1, key2=2, key3=3]
      println(numMap["key1"]) // 1
      
    3. 集合拷贝:toList()、toMutableList()、toSet()

      val sourceList = mutableListOf<BookModel>()
      for (i in 0..1) {
      sourceList.add(BookModel(i, "Android"))
      }
      
      // sourceList:[BookModel(id=0, name=Android), BookModel(id=1, name=Android)]
      val copyList = sourceList.toMutableList()
      
      //sourceList新增数据
      sourceList.add(BookModel(100, "IOS"))
      /*
      sourceList:
      [BookModel(id=0, name=Android), BookModel(id=1, name=Android), BookModel(id=100, name=IOS)]
      copyList:
      [BookModel(id=0, name=Android), BookModel(id=1, name=Android)]
      */
      
      // sourceList修改数据
      sourceList.forEachIndexed { index, bookModel ->
      	bookModel.name = "Andx$index"
      }
      /*
      sourceList:
      [BookModel(id=0, name=Andx0), BookModel(id=1, name=Andx1), BookModel(id=100, name=Andx2)]
      copyList:
      [BookModel(id=0, name=Andx0), BookModel(id=1, name=Andx1)]
      */
      
      

      toList()、toMutableList()、toSet() 标准库中的集合复制操作创建了具有相同元素引用的 浅复制集合。 因此,对源集合元素所做的更改会反映在其所有副本中,如果对源集合进行添加或删除元素,则不会影响副本

    4. 集合转换:map()、mapNotNull()、mapIndexed()、mapIndexedNotNull()、mapKeys{}、mapValues{}

      val numbers = listOf("one", "two", "three")
      val mIndexes = listOf(1, 2, 3)
      val mTwoIndex = listOf(1, 2)
      /**
       * ----------------map()映射-以下作用于list,返回的也都是list----------------
       */
      //map()、mapNotNull()映射函数,区别是mapNotNull()会过滤掉结果为null的值
      println(numbers.map { "it's $it" }) //[it's one, it's two, it's three]
      println(numbers.mapNotNull { if (it.length == 3) null else it }) //[three]
      
      //mapIndexed()、mapIndexedNotNull()带有元素索引位置的映射函数,mapIndexedNotNull()会过滤掉结果为null的值
      println(numbers.mapIndexed { index, s -> "$index-$s" }) //[0-one, 1-two, 2-three]
      println(numbers.mapIndexedNotNull { index, s ->
      	if (s.length == 3) null else "$index-$s" //[2-three]
      })
      /**
       *----------------以下作用于map,返回的也都是map----------------
       */
      //mapKeys() & mapValues()
      val numMap = mapOf("one" to 1, "two" to 2, "three" to 3)
      println(numMap) //{one=1, two=2, three=3}
      println(numMap.mapKeys { it.key.uppercase(Locale.ROOT) }) //{ONE=1, TWO=2, THREE=3}
      println(numMap.mapValues { it.value + it.key.length }) //{one=4, two=5, three=8}
      
    5. 集合转换:zip()、unzip()

      /**
       * ----------------zip()合拢----------------
       */
      //zip()操作。如果集合的大小不同,则 zip() 的结果为较小集合的大小
      println(numbers.zip(mIndexes)) //[(one, 1), (two, 2), (three, 3)]
      println(numbers zip mTwoIndex) //中缀表达式方式  [(one, 1), (two, 2)]
      
      //zip()中第2个参数为转换函数的使用举例
      println(numbers.zip(mIndexes) { number, index -> "number:$number index:$index" })
      //执行结果:[number:one index:1, number:two index:2, number:three index:3]
      
      //unzip()函数
      val numPairs: List<Pair<String, Int>> = listOf("one" to 1, "two" to 2, "three" to 3)
      println(numPairs.unzip()) //([one, two, three], [1, 2, 3])
      println(numPairs.unzip().first) //[one, two, three]
      println(numPairs.unzip().second) //[1, 2, 3]
      
    6. 集合转换:associateWith()、associateBy()、associate()

      /** 
       * xxxWith()
       * 返回一个Map,其中键是给定集合中的元素,值是应用于每个元素的valueSelector函数产生的。
       * 如果任意两个元素相等,则将最后一个元素添加到映射中。
       * 返回的映射保留了原始集合的条目迭代顺序。 
       */
      println(numbers.associateWith { it.length }) //{one=3, two=3, three=5}
      
      /**
       * 返回一个Map,从给定集合中遍历,将keySelector函数(即{}内部分)作为key,将元素作为value来构建Map,。
       * 如果任何两个元素具有由keySelector返回的相同键,则最后一个元素将被添加到映射中。
       * 返回的映射保留了原始集合的条目迭代顺序。
       */
      // xxx.associateWith{ ~ } = xxx.associateBy( {it}, {~} )
      
      //associateBy将元素作为value来构建Map
      println(numbers.associateBy { it.first() }) //{o=one, t=three},原本为{o=one, t=two, t=three},由于key=t时重复,所以t=two被覆盖
      var student01 = Student("小明","宁波",22)
      var student02 = Student("小红","浙江",22)
      var student03 = Student("小黄","绍兴",22)
      var studentList = mutableListOf<Student>(student01,student02,student03)
      var mapyy = studentList.associateBy { it.name }
      //{小明=Student{name='小明', address='宁波', age=22}, 小红=Student{name='小红', address='浙江', age=22}, 小黄=Student{name='小黄', address='绍兴', age=22}}
      
      //自行设计key和value
      println(numbers.associateBy(
          keySelector = { it.first() }, valueTransform = { it.length }) //{o=3, t=5}
      // 等价于{ it.first() },{ it.length }
      )
      // 等价于:
      println(numbers.associate { it.first() to it.length }) // {o=3, t=5}
      
      
    7. 集合转换、扁平化:flatten()、flatMap()

    /**
    * ----------------flatten()、flatMap()----------------
    * flatten()返回嵌套集合集合中的所有元素的List
    * flatMap()需要一个函数将一个集合元素映射到另一个集合。返回单个列表其中包含所有元素的值。等于map()+flatten()的连续调用
    */
    val containers = listOf(
      listOf("one", "two"),
      listOf("three", "four", "five")
    )
    println(containers.flatten()) //[one, two, three, four, five]
    println(containers.flatMap { subs ->
      listOf(subs)  //[[one, two], [three, four, five]]
    })
    
    1. 集合过滤:filter()、filterTo、filterNot()和filterNotTo():
     //不会影响原始集合数据,而是产生一个新的集合
     val numbers = mutableListOf("one", "two", "three", "four")
     val filterNums = numbers.filter { it.length > 3 }
     println("numbers: $numbers") //numbers: [one, two, three, four]
     println("filterNums:$filterNums")  //filterNums:[three, four]
    
    //filterTo()函数会遍历数组内的元素,验证每个元素是否符合predicate函数,如果符合就把它加到目标集合filterResults
    val filterResults = mutableListOf("1", "2")
    numbers.filterTo(filterResults, { it.length > 3 })
    println("numbers: $numbers") //numbers: [one, two, three, four]
    println("filterResults:$filterResults")  //filterResults:[1, 2, three, four]
    
    // filterNot()和filterNotTo():与filter相反,这两个函数会过滤出不符合条件的元素;
    
    //写操作 
    //对于可变集合,还存在可更改集合状态的`写操作` 。这些操作包括`添加、删除和更新元素`。
    //对于某些操作,有成对的函数可以执行相同的操作:`一个函数就地应用该操作,另一个函数将结果作为单独的集合返回`
    val sortedNums = numbers.sorted()
    println("numbers: $numbers") //numbers: [one, two, three, four]
    println("sortedNums:$sortedNums") //numbers: [one, two, three, four]  所以sorted()没有改变原始集合
    numbers.sort()
    println("numbers: $numbers") //numbers: [one, two, three, four]  所以sort()直接在原始集合上进行改动
    
  • filter()不对原集合进行改变,而是生成一个新的,所有的改变是在新的里改

2022-05-20

  • 集合遍历:rangeTo、until、downTo、step、forEach、forEachIndexed

    //正向迭代
    val numbers = mutableListOf("one", "two", "three", "four")
    for (pos in 0..3) {//表示0<=pos && pos<=3,即pos在[0,3]内;或者用numbers.indices
    	print("${numbers[pos]} ") // one two three four
    }
    //如果是左闭右开区间[0,4),使用until关键字
    for (pos in 0 until 4) {
    	print("${numbers[pos]} ") // one two three four
    }
    numbers.forEach { print("$it ") } //one two three four
    numbers.forEachIndexed { index, number -> println("index:$index, number:$number") }
    /**
    * index:0, number:one
    * index:1, number:two
    * index:2, number:three
    * index:3, number:four
    */
    
    //反向迭代
    for (pos in 3 downTo 0) {
    	print("${numbers[pos]} ") //four three two one
    }
    
    //任意步长迭代, 如下面的步长为2
    for (pos in 0..3 step 2) {
    	print("${numbers[pos]} ") //one three
    }
    
    
  • 集合加减:

    val numbers = listOf("one", "two", "three", "three")
    val plusNumbers = numbers + "four"
    println(numbers) //原始集合:[one, two, three, three]
    println(plusNumbers) //集合加操作:[one, two, three, three, four]
    
    val minusNum1 = numbers - listOf("three")
    val minusNum2 = numbers - "three"
    println(numbers) //原集不变:[one, two, three, three]
    println(minusNum1)//集合减操作1:[one, two]
    println(minusNum2) //集合减操作2:[one, two, three]
    //注意:minus操作,如果第二个操作数是一个元素,那么 minus 移除其在原始集合中的 第一次 出现;
    // 如果是一个集合,那么移除其元素在原始集合中的所有出现。
    
  • 集合分组:groupBy()、groupingBy()

    val numbers = listOf("one", "two", "three")
    //groupBy() 使用lambda函数并返回一个Map。在此Map中,每个键都是lambda结果,而对应的值是返回此结果的元素 List。
    //在带有两个lambda的groupBy()结果Map中,由keySelector函数生成的键映射到值转换函数的结果,而不是原始元素。
    println(numbers.groupBy { it.first() })//{o=[one], t=[two, three]}
    println(numbers.groupBy(keySelector = { it.length }, valueTransform = { it }))
    //{3=[one, two], 5=[three]}
    
    //https://www.kotlincn.net/docs/reference/collection-grouping.html
    println(numbers.groupingBy { it.first() }.eachCount()) //{o=1, t=2, f=2, s=1}
    
  • 取集合的部分:slice()、take()、drop()、chunked()、windowed()、zipWithNext()

    val numbers = listOf("one", "two", "three", "four")
    
    //slice() 切割
    println(numbers.slice(1..2)) //slice(indices: IntRange) 执行结果:[two, three]
    println(numbers.slice(0..3 step 2)) //slice(indices: IntRange)间隔2,执行结果:[one, three]
    println(numbers.slice(listOf(1, 2))) //slice(indices: Iterable<Int>) 执行结果:[two, three]
    
    //take() & drop()
    //take:从头开始获取指定数量的元素; takeLast:从尾开始获取指定数量的元素
    println(numbers.take(2)) //[one, two]
    println(numbers.takeLast(2)) //[three, four]
    println(numbers.drop(1)) //[two, three, four]
    println(numbers.dropLast(2))//[one, two]
    
    //takeWhile() & dropWhile()
    //takeWhile()不停获取元素直到排除与谓词匹配的首个元素。如果首个集合元素与谓词匹配,则结果为空。
    println(numbers.takeWhile { !it.startsWith("f") })//[one, two, three]
    println(numbers.takeLastWhile { !it.startsWith("t") })//[four]
    //dropWhile()将首个与谓词不匹配的元素返回到末尾
    println(numbers.dropWhile { it.length == 3 }) //[three, four]
    println(numbers.dropLastWhile { it.length > 3 })//[one, two]
    
    //chunk():要将集合分解为给定大小的“块”,最后一个块的大小可能较小
    val nums = (0..7).toList()
    println(nums) //[0, 1, 2, 3, 4, 5, 6, 7]
    println(nums.chunked(3)) //List<List<T>>类型: [[0, 1, 2], [3, 4, 5], [6, 7]]
    //还可以对返回的块进行转换,如下:
    println(nums.chunked(3) { it.sum() }) //[3, 12, 13]
    
    val numWindows = (0..7).toList()
    println(numWindows.windowed(3)) //[[0, 1, 2], [1, 2, 3], [2, 3, 4], [3, 4, 5], [4, 5, 6], [5, 6, 7]]
    //step:定义相邻两个窗口第一个元素之间的距离 partialWindows:是否包含最后较少元素,true包含 false不包含
    println(numWindows.windowed(3, step = 2, partialWindows = true))
    //执行结果:[[0, 1, 2], [2, 3, 4], [4, 5, 6], [6, 7]]
    println(numWindows.windowed(3) { it.sum() }) //[3, 6, 9, 12, 15, 18]
    
    //zipWithNext()单独创建两个元素的窗口
    println(numWindows.zipWithNext()) //List<Pair<T, T>>执行结果:[(0, 1), (1, 2), (2, 3), (3, 4), (4, 5), (5, 6), (6, 7)]
    println(numWindows.zipWithNext { s1, s2 -> s1 * s2 }) //List<R>执行结果:[0, 2, 6, 12, 20, 30, 42]
    
    
  • 集合聚合操作:maxOrNull()、minOrNull()、average()、sum()、sumBy()、reduce()、fold()

    val numbers = listOf(30, 20, 40, 10)
    println(numbers.count()) //元素数量 4
    println(numbers.maxOrNull()) //最大元素 40
    println(numbers.minOrNull()) //最小元素 10
    println(numbers.average()) //平均值 25.0
    println(numbers.sum())//集合元素的总和 100
    
    //接受一个选择器函数并返回使选择器返回最大或最小值的元素。
    val min3Remainder = numbers.minByOrNull { it % 3 } //30
    val max3Remainder = numbers.maxByOrNull { it % 3 } //20
    //接受一个 Comparator 对象并且根据此 Comparator 对象返回最大或最小元素
    val maxNum = numbers.maxWithOrNull(compareBy { it }) //40
    val minNum = numbers.minWithOrNull(compareBy { it }) //10
    println(min3Remainder) //30
    println(max3Remainder) //20
    println(maxNum) //40
    println(minNum) //10
    
    println(numbers.sumBy { it * 2 }) //对lambda函数返回的Int结果进行求和 200
    println(numbers.sumByDouble { it.toDouble() / 2 }) //对lambda函数返回的Double结果进行求和 50.0
    
    // fold() & reduce() 依次将所提供的操作应用于集合元素并返回累积的结果。
    // 区别:fold() 接受一个初始值并将其用作第一步的累积值,而reduce()的第一步则将第一个和第二个元素作为第一步的操作参数。
    val sum = numbers.reduce { sum, element -> sum + element }
    println(sum) //100
    
    //val numbers = listOf(30, 20, 40, 10)
    //reduce()
    val sumDouble = numbers.reduce { sum1, element ->
    	print("$sum1 ") //30 70 150 170
    	sum1 + element * 2
    }
    println(sumDouble) //170
    //reduceRight() 操作参数会更改其顺序:第一个参数变为元素,然后第二个参数变为累积值。
    val sumDoubleRight = numbers.reduceRight { element, sum2 ->
    print("$sum2 ") //10 90 130 190
    	sum2 + element * 2
    }
    println(sumDoubleRight) //190
    //reduceIndexed()
    val sumIndex = numbers.reduceIndexed { index, sumI, element ->
    	print("index:$index,sum:$sumI,element:$element ")
        /**
        * index:1,sum:30,element:20
        * index:2,sum:50,element:40
        * index:3,sum:90,element:10
        */
    	sumI + element
    }
    println(sumIndex)
    //reduceRightIndexed()
    val sumIndexRight = numbers.reduceRightIndexed { index, sumR, element ->
        print("index:$index,sum:$sumR,element:$element ")
        /**
        * index:2,sum:40,element:10
        * index:1,sum:20,element:50
        * index:0,sum:30,element:70
        */
        sumR + element
    }
    println(sumIndexRight) //100
    
    //注:为了防止为null时抛异常,可以使用对应的reduceOrNull()、
    // reduceRightOrNull()、reduceIndexedOrNull()、reduceRightIndexedOrNull()
    
    //val numbers = listOf(30, 20, 40, 10)
    //fold()
    val sumDouble1 = numbers.fold(0) { sum3, element ->
        print("$sum3 ") //0 60 100 180 200
        sum3 + element * 2
    }
    println(sumDouble1) //200
        //foldRight() 操作参数会更改其顺序:第一个参数变为元素,然后第二个参数变为累积值。
        val sumDoubleRight1 = numbers.foldRight(0) { element, sum4 ->
        print("$sum4 ") //0 20 100 140 200
        sum4 + element * 2
    }
    println(sumDoubleRight1) //200
    
    
  • 集合排序

    //Comparable进行比较
    println(Version(1, 2) > Version(1, 3)) //false
    println(Version(2, 0) > Version(1, 5)) //true
    
    val origins = listOf("aaa", "c", "bb")
    //sortedWith() + Comparator自定义顺序进行比较
    val lengthComparator = Comparator { o1: String, o2: String -> o1.length - o2.length }
    println(origins.sortedWith(lengthComparator)) //[c, bb, aaa]
    println(origins) //[aaa, bb, c]
    //继续简化
    println(origins.sortedWith(compareBy { it.length })) //[c, bb, aaa]
    //上述sortedWith(compareBy{})代码简写为sortedBy{}
    println(origins.sortedBy { it.length }) //[c, bb, aaa]
    println(origins.sortedByDescending { it.length }) //[aaa, bb, c]
    
    //自然顺序
    println(origins.sorted()) //[aaa, bb, c]
    println(origins.sortedDescending()) //[c, bb, aaa]
    
    //倒序 reversed()产生新集合,改变原始集合不会影响新集合;
    val originStrs = mutableListOf("aaa", "c", "bb")
    println(originStrs.reversed()) //使用reversed()倒序 执行结果:[bb, c, aaa]
    //使用asReversed()倒序
    val asReverseList = originStrs.asReversed()
    println(asReverseList) //[bb, c, aaa]
    originStrs.add("dd") //对原始集合进行改动
    println(asReverseList) //原始集合变化,倒序的集合也自动更新了 执行结果:[dd, bb, c, aaa]
    
    //随机顺序
    val shuffledNums = listOf("one", "two", "three")
    println(shuffledNums.shuffled())//随机产生一个新的集合
    println(shuffledNums) //[one, two, three]
    
    

2022-05-23

  • 有关Mapper的相关用法:

    • baseMapper.deleteByMap

      @MultiTransactional(values = [MYBATIS_TRANSACTION])
      @NoRepeatSubmit
      @LogParams(module = "人员标签", operate = "删除人员标签类型")
      override fun deleteTagType(type: String, module: String?) {
          baseMapper.deleteByMap(
              mapOf(
                  PersonTag::module.name to if (module.isNullOrBlank()) INSIDER else module,
                  PersonTag::type.name to type
              )
          )
      }
      
    • ktQuary()

      override fun getPersonRelateTypeList(type: String?, keywords: String?): List<PersonRelateType> {
      	val typeIsNullOrBlank = type.isNullOrBlank()
      	val keywordsIsNullOrBlank = keywords.isNullOrBlank()
      	// 若type,keyword为null或者空,都是查全部
      	return ktQuery().eq(!typeIsNullOrBlank, PersonRelateType::sourceType, type)
      		.like(!keywordsIsNullOrBlank, PersonRelateType::name, keywords).list()
      }
      
    • mongoTemplate.findAll(…)

      mongoTemplate.findAll(Organization::class.java)
      
    • mongoTemplate.find(…)

      mongoTemplate.find(
          Query(
              Criteria.where("systemFieldList").elemMatch(
              	Criteria.where(FieldConst.CODE).`is`(FieldConst.ORG_TYPE_NAME).and("values.value")
              		.regex(keywords)
              )
          ), OrgTypeConfig::class.java
      )
      

2022-05-24

  • Ubuntu账号:yangyuanjie

  • Docker:990432004

  • maven打包:在这里插入图片描述

2022-05-25

  • gt: greater than 大于

    gte: greater than or equal 大于等于

    lt: less than 小于

    lte: less than or equal 小于等于

  • kotlin中的when区别于java的switch,不用break,更像是许多个同级的if;

    fun main(args: Array<String>){
        var number = 4
        var numberProvided = when(number) {
            1 -> "One"
            2 -> "Two"
            3 -> "Three"
            4 -> "Four"
            5 -> "Five"
            else -> "invalid number"
        }
        println("You provide $number")
    }
    
  • kotlin中的函数参数可以设置默认值,如果调用时不传参、传一半、使用命名参数,则使用默认值

    fun main(args: Array<String>) {
        run()
        run(latter='s') // 命名参数
    }
    fun run(num:Int= 5, latter: Char ='x'){
        print("parameter in function definition $num and $latter")
    }
    
  • 人体大约有140~160亿个神经细胞,其中大约有100亿是存在于大脑当中,而且大脑中的神经元一般是不可再生的,随着年龄的增加会逐渐减少。

2022-05-27

  • 据台媒报道,台湾初中及小学即将于8月起将所谓“本土语言”纳入必修课,各校纷纷展开调查,了解学生想学习哪些方言。有岛内家长透露,家中就读小学的孩子近日从学校带回一份“本土语文课程调查表”,当中竟罗列49种方言类别,让人无所适从。新北市一名家长看到表格后也直呼“十分傻眼”,并称不仅无法理解师资与教材何来,更无法接受学校得受迫为政治意识形态服务,耗费时间与资源开办此类毫无意义的语言课,“根本不是为了孩子着想!”还有家长表示,该表格当中的少数民族语言,光是排湾部落语就有北、中、南、东4种,卑南部落语有4种,布农部落语有5种,学生如果真的选了,学校排得出师资和教材吗?
  • 据台媒早前报道,民进党当局是在2021年通过“语言发展法”,将闽南语、客家话、台湾少数民族语言等所谓“本土语言”列入中小学必修课的,有民进党籍“立委”称“这是历史性一刻”。但根据媒体统计,截至2017年4月,经台湾官方认证的少数民族部落达到16个之多,但人口总数只有55万4925人,约占台湾总人口数的2.36%。人口比例如此之低,其语言却能成为“本土语言”,官方位阶甚至高于闽南语,对此,岛内外有识之士纷纷痛批,民进党当局不过是借建构所谓“原民史观”来包装“台独史观”,搞“文化台独”才是真实面目。台湾过去自诩传承中华传统文化,现在却是大力施行“去中国化”,台湾在民进党当局“执政”下,目光越来越短浅,毫无前景可言!

看着一个个利用民族主义获取名望和利益的人和媒体被民族主义暴锤,还有什么比这件事情本身更有教育意义?

允许批判质疑,采纳合理意见

允许批判质疑,冷处理、无视,允许各种意见自由发酵

允许批判质疑,冷处理,清除不同意见

不允许批判质疑,允许沉默

不允许批判质疑,不允许沉默,只能赞扬歌颂、配合表演

不允许批判质疑,不允许沉默,只能卖力地、真诚地歌颂

  • ……?

2022-05-30

  • 解释一下extension函数

扩展(extension)函数用来对class的扩展,而不需要从class进行派生。

  • Array是 Integer[]在引擎盖下,而 IntArray是 int[]

2022-06-01

  • 线程池:java通过Executors提供四种线程池,分别为:

    • newCachedThreadPool

      /**
       * 可缓存无界线程池测试
       * 当线程池中的线程空闲时间超过60s则会自动回收该线程,核心线程数为0
       * 当任务超过线程池的线程数则创建新线程。线程池的大小上限为Integer.MAX_VALUE,
       * 可看做是无限大。
       */
      @Test
      public void cacheThreadPoolTest() {
          // 创建可缓存的无界线程池,可以指定线程工厂,也可以不指定线程工厂
          ExecutorService es = Executors.newCachedThreadPool(new testThreadPoolFactory("cachedThread"));
          for (int i = 0; i < 10; i++) {
              es.submit(() -> {
                  print("cachedThreadPool");
                  System.out.println(Thread.currentThread().getName());
                      }
              );
          }
      }
      
    • newFixedThreadPool

      /**
       * 创建固定线程数量的线程池测试
       * 创建一个固定大小的线程池,该方法可指定线程池的固定大小,对于超出的线程会在LinkedBlockingQueue队列中等待
       * 核心线程数可以指定,线程空闲时间为0
       */
      @Test
      public void fixedThreadPoolTest() {
          ExecutorService executorService = Executors.newFixedThreadPool(5, new testThreadPoolFactory("fixedThreadPool"));
          for (int i = 0; i < 10; i++) {
              executorService.submit(() -> {
                          print("fixedThreadPool");
                          System.out.println(Thread.currentThread().getName());
                      }
              );
          }
      }
      
    • newScheduledThreadPool

      /**
       * 创建定时周期执行的线程池测试
       *
       * schedule(Runnable command, long delay, TimeUnit unit),延迟一定时间后执行Runnable任务;
       * schedule(Callable callable, long delay, TimeUnit unit),延迟一定时间后执行Callable任务;
       * scheduleAtFixedRate(Runnable command, long initialDelay, long period, TimeUnit unit),延迟一定时间后,以间隔period时间的频率周期性地执行任务;
       * scheduleWithFixedDelay(Runnable command, long initialDelay, long delay,TimeUnit unit),与scheduleAtFixedRate()方法很类似,
       * 但是不同的是scheduleWithFixedDelay()方法的周期时间间隔是以上一个任务执行结束到下一个任务开始执行的间隔,而scheduleAtFixedRate()方法的周期时间间隔是以上一个任务开始执行到下一个任务开始执行的间隔,
       * 也就是这一些任务系列的触发时间都是可预知的。
       * ScheduledExecutorService功能强大,对于定时执行的任务,建议多采用该方法。
       *
       * 作者:张老梦
       * 链接:https://www.jianshu.com/p/9ce35af9100e
       * 来源:简书
       * 著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
       */
      @Test
      public void scheduleThreadPoolTest() {
          // 创建指定核心线程数,但最大线程数是Integer.MAX_VALUE的可定时执行或周期执行任务的线程池
          ScheduledExecutorService executorService = Executors.newScheduledThreadPool(5, new testThreadPoolFactory("scheduledThread"));
      
          // 定时执行一次的任务,延迟1s后执行
          executorService.schedule(new Runnable() {
              @Override
              public void run() {
                  print("scheduleThreadPool");
                  System.out.println(Thread.currentThread().getName() + ", delay 1s");
              }
          }, 1, TimeUnit.SECONDS);
      
      
          // 周期性地执行任务,延迟2s后,每3s一次地周期性执行任务
          executorService.scheduleAtFixedRate(new Runnable() {
              @Override
              public void run() {
                  System.out.println(Thread.currentThread().getName() + ", every 3s");
              }
          }, 2, 3, TimeUnit.SECONDS);
      
      
          executorService.scheduleWithFixedDelay(new Runnable() {
              @Override
              public void run() {
                  long start = new Date().getTime();
                  System.out.println("scheduleWithFixedDelay 开始执行时间:" +
                          DateFormat.getTimeInstance().format(new Date()));
                  try {
                      Thread.sleep(5000);
                  } catch (InterruptedException e) {
                      e.printStackTrace();
                  }
                  long end = new Date().getTime();
                  System.out.println("scheduleWithFixedDelay执行花费时间=" + (end - start) / 1000 + "m");
                  System.out.println("scheduleWithFixedDelay执行完成时间:"
                          + DateFormat.getTimeInstance().format(new Date()));
                  System.out.println("======================================");
              }
          }, 1, 2, TimeUnit.SECONDS);
      
      }
      
    • newSingleThreadExecutor:创建一个单线程化的线程池,它只会用唯一的工作线程来执行任务,保证所有任务按照指定顺序 (FIFO, LIFO, 优先级)执行。

    2022-06-02

  • 线程通信相关:wait/notify/notifyAll

    • 一定在synchronized中使用wait()/notify()/notifyAll(),也就是说一定要先获取锁,这个前面我们讲过,因为只有加锁后,才能获得监视器。否则jvm也会抛出IllegalMonitorStateException异常。
    • 使用wait()时,判断线程是否进入wait状态的条件一定要使用while而不要使用if,因为等待线程可能会被错误地唤醒,所以应该使用while循环在等待前等待后都检查唤醒条件是否被满足,保证安全性。
    • notify()或notifyAll()方法调用后,线程不会立即释放锁。调用只会将wait中的线程从等待队列移到同步队列,也就是线程状态从waitting变为blocked;
    • 从wait()方法返回的前提是线程重新获得了调用对象的锁。
  • start()方法真正实现了多线程运行,调用 start() 方法来启动线程时会自动调用 run();而直接调用 run() 方法就直接执行,达不到多线程运行的目的。

  • 一个线程对线的 start() 方法只能调用一次,多次调用会抛出 java.lang.IllegalThreadStateException 异常;run() 方法没有限制。

2022-06-06

  • SQL(Structured Query Language,结构化查询语言),对数据库进行查询和修改操作的语言SQL 语言是目前广泛使用的关系数据库标准语言,是各种数据库交互方式的基础。

  • 常用数据库访问接口简介

    • Java Data Base(JDBC,Java 数据库连接)用于 Java 应用程序连接数据库的标准方法,是一种用于执行 SQL 语句的 Java API,可以为多种关系数据库提供统一访问,它由一组用 Java 语言编写的类和接口组成;
    • ODBC(Open Database Connectivity,开放数据库互连)为访问不同的 SQL 数据库提供了一个共同的接口。ODBC 使用 SQL 作为访问数据的标准。这一接口提供了最大限度的互操作性。一个应用程序可以通过共同的一组代码访问不同的 SQL 数据库管理系统;
  • CHAR 和 VARCHAR 类型

    • CHAR(M) 为固定长度字符串,在定义时指定字符串列长。当保存时,在右侧填充空格以达到指定的长度。M 表示列的长度,范围是 0~255 个字符。
    • VARCHAR(M) 是长度可变的字符串,M 表示最大列的长度,M 的范围是 0~65535。VARCHAR 的最大实际长度由最长的行的大小和使用的字符集确定,而实际占用的空间为字符串的实际长度加 1
  • http://start.aliyun.com阿里云SprngBoot初始化镜像节点

  • SpringBoot整合Mybatis-Plus,首先需要在SpringBootApplication中添加注释:

    @SpringBootApplication(scanBasePackages = {"com.example.demo"})	// controller、service等
    @MapperScan("com.example.demo.mapper")	// mapper映射
    public class DemoApplication {
        public static void main(String[] args) {
            SpringApplication.run(DemoApplication.class, args);
        }
    }
    

2022-06-07

  • ESC:

    image-20220607103715856
  • 阿里云数据库:121.199.210.138:3306

    • username=horseweed
    • password=YANGyuanJIE#3
  • 腾讯轻量应用服务器

2022-06-14

  • Spring注解@Transactional:在代码执行出错的时候能够进行事务的回滚。
  • 使用说明:
    • 在启动类上添加@EnableTransactionManagement注解。
    • 用于类上时,该类的所有 public 方法将都具有该类型的事务属性,同时,我们也可以在方法级别使用该标注来覆盖类级别的定义。
    • 在项目中,@Transactional(rollbackFor=Exception.class),如果类加了这个注解,那么这个类里面的方法抛出异常,就会回滚,数据库里面的数据也会回滚。
    • 在@Transactional注解中如果不配置rollbackFor属性,那么事物只会在遇到RuntimeException的时候才会回滚,加上rollbackFor=Exception.class,可以让事物在遇到非运行时异常时也回滚。

你好我想问一下权限需要以哪种方式重写?

1、后端把角色权限传给前端,前端根据权限进行页面的筛选,让用户只能看到/访问权限内的页面;

(使用者如果懂计算机,可通过接口测试工具访问被隐藏界面的接口)

2、前端不变,即admin显示所有界面,user仅显示个人界面,在后端对传入接口的权限做判断,过滤越权请求;

(当admin/user权限改变时,显示页面数量不会发生改变;用户可能点击某个页面没反应)

3、后端把权限给前端,前端先做页面筛选,后端再进行权限判断;

2022-06-17

  • navicat暴力修改注册表

    1.关闭Navicat Premium 12;

    2.然后 win+R,输入regedit 回车,打开注册表编辑器;

    3.展开HKEY_CURRENT_USER\Software\PremiumSoft\Data
    展开子目录,如果里面只包含一个名为Info的文件夹,就删掉它。

    4.展开HKEY_CURRENT_USER\Software\Classes\CLSID
    展开子目录,如果里面只包含一个名为Info的文件夹,就删掉它。

    5.关闭注册表,重新打开Navicat Premium 12即可。

  • // 公司官方库:
    JtBeanUtil.map2Bean()	// map转bean
    orgCommonService.getFirstRepeatFieldByCode
    
  • 设置权限拦截器框架

  • 梳理角色设置相关接口及其对应字段,添加超级管理员判断

  • 设置权限实际URI与枚举字段匹配

  • request.getMethod()	//获取地址
    
  • 当访问 Servlet 时,所有请求消息将被封装到 HttpServletRequest 对象中,请求消息的请求行中包含请求方法、请求资源名、请求路径等信息,为了获取这些信息,HttpServletRequest 接口定义了一系列方法,如表 1 所示。

    方法声明 功能描述
    String getMethod() 该方法用于获取 HTTP 请求消息中的请求方式(如 GET、POST 等)
    String getRequestURI() 该方法用于获取请求行中的资源名称部分即位于 URL 的主机和端门之后、参数部分之前的部分
    String getQueryString() 该方法用于获取请求行中的参数部分,也就是资源路径后问号(?)以后的所有内容
    String getContextPath() 该方法用于获取请求 URL 中属于 Web 应用程序的路径,这个路径以 / 开头,表示相对于整个 Web 站点的根目录,路径结尾不含 /。如果请求 URL 属于 Web 站点的根目录,那么返回结果为空字符串("")
    String getServletPath() 该方法用于获取 Servlet 的名称或 Servlet 所映射的路径
    String getRemoteAddr() 该方法用于获取请求客户端的 IP 地址,其格式类似于 192.168.0.3
    String getRemoteHost() 该方法用于获取请求客户端的完整主机名,其格式类似于 pcl.mengma.com。需要注意的是,如果无法解析出客户机的完整主机名,那么该方法将会返回客户端的 IP 地址
    int getRemotePort() 该方法用于获取请求客户端网络连接的端口号
    String getLocaIAddr() 该方法用于获取 Web 服务器上接收当前请求网络连接的 IP 地址
    String getLocalName() 该方法用于获取 Web 服务器上接收当前网络连接 IP 所对应的主机名
    int getLocalPort() 该方法用于获取 Web 服务器上接收当前网络连接的端口号
    String getServerName() 该方法用于获取当前请求所指向的主机名,即 HTTP 请求消息中 Host 头字段所对应的主机名部分
    int gctServcrPort() 该方法用于获取当前请求所连接的服务器端口号,即 HTTP 请求消息中 Host 头字段所对应的端口号部分
    StringBuffcr getRequestURL() 该方法用于获取客户端发出请求时的完整 URL,包括协议、服务器名、端口号、 资源路径等信息,但不包括后面的査询参数部分。注意,getRequcstURL() 方法返冋的结果是 StringBuffer 类型,而不是 String 类型,这样更便于对结果进行修改

2022-06-22

  • 添加superAdmin后,系统默认角色设置,数据结构由原先Boolean改为String;

  • 整合权限,重写check接口;梳理authox中admin与superadmin之间的关系,role与name的关系,整合权限,重写check接口(原系统authoxrolemap表与authoxrule表中role字段不对应,导致authox自带接口错位无法匹配)

  • 需求会议:组织架构系统对外接口定制;

  • 组织架构系统对外服务接口

  • 梳理原有组织树筛选逻辑,限定user用户可见范围,因为项目角色增加了superAdmin角色,导致判断逻辑更改(Boolean转为String)

  1. 打包:Maven-clean;Maven-Package;

  2. Dockerfile:打开于-终端

    • image-20220622105316944

    • docker build -t harbor.sucsoft.com:30002/cowork/yunnan-organization:1.6.8 ./
      docker push harbor.sucsoft.com:30002/cowork/yunnan-organization:1.6.8
      // docker push harbor.sucsoft.com:30002/cowork/yunnan-organization:1.6.8 (镜像名)
      
    • image-20220622105723033

    • 镜像名从harbor上获取地址:harbor-项目-cowork-镜像仓库-项目

    • image-20220622112851652
    • image-20220622112926901
  3. hardon首页-进入项目-新增版本

    • image-20220623134822052
  4. 后在dev-编辑

    • image-20220623102701141
  5. waterloo:进入

    • image-20220622172722927
    • 在地址后加+
    • image-20220622172102795

2022-06-23

  • List<Organization> organizationList = mongoTemplate.find(Query.query(Criteria.where("orgId").is(currentPersonInfo.getOrgId())),Organization.class);
    
  • image-20220626224246850

2022-06-26

知识空间 (sucsoft.com)

  • MockHttpServer

快捷的构建一个测试用的 http 服务器。这个服务器可以用来当作微服务测试中需要的被依赖服务。

  • basics
import com.cgs.test.execute
import com.cgs.test.get
import com.cgs.test.getFreeServerPort
import com.cgs.test.mockHttpServer
import org.http4k.core.Method
import org.http4k.core.Request

...

    val mockPort = getFreeServerPort()
    lateinit var server: MockHttpServer

    beforeSpec {
        // 定义一个 http 服务器,监听 mockPort 端口
        server = mockHttpServer(mockPort) {
            // 一条路由规则,GET 任意 uri 请求都返回 hello world!
            get("/**") { "hello world!" }
        }
        server.start()
    }

    afterSpec {
        server.stop()
    }

    test("some test") {
        assertTrue {
            Request(Method.GET, "http://localhost:$mockPort/api")
                .execute()
                .bodyString() == "hello world!"
        }     
    }
  • 动态路由

服务已经启动后,还可以动态修改路由规则:

    beforeSpec {
        server = mockHttpServer(mockPort) {
            // 原先的路由规则
            get("/hello") { "hello world!" }
        }
        server.start()
    }

    afterSpec {
        server.stop()
    }

    test("some test") {
        assertTrue {
            Request(Method.GET, "http://localhost:$mockPort/api")
                .execute()
                .bodyString() == "hello world!"
        }
        // 重新定义路由规则
        server.route {
             // 新的路由规则
             get("/foo") { "bar" }
        }
        assertTrue {
            // 旧的路由已经失效
            Request(Method.GET, "http://localhost:$mockPort/hello")
                .execute()
                .status == Status.NOT_FOUND
        }
        assertTrue {
            Request(Method.GET, "http://localhost:$mockPort/foo")
                .execute()
                .bodyString() == "bar"
            }
        }   
    }
  • 找出hasNext为false的,然后插插插

  • 八股

  • TM1: 用 Kotlin 编程

  • TM2: kotest + kotlin-test + junit(5) + surefire

  • TM3: Spring Test

  • TM4: 提取 interface

  • TM5: 单方法依赖

    关于 Spring 的一些建议

  • TM6: 高阶函数优先于注解

  • TM7: 不使用 setter 来做 DI

  • TM8: 尽量不借助 autoscan

  • TM9: 不是所有单例都需要定义成 Spring Bean

  • Mock!

    无论程序结构做得多好,指望不写 mock 就能进行单元测试都是妄想。单元测试之所以不同于集成测试,就是被测试的代码是拿出集成运行环境以外的。要做好单元测试,首先要从心理上乐于写 mock。在单元测试中使用 mock 框架是允许的

  • TM10: Mock DAO

  • TM11: 测试用例的位置

    test 目录下,测试代码仍然要放在合理的 package 内。package 名字至少要包含 test。例如 com.cgs.deh.catalog.test

    测试用例的文件名/类名/对象名必须以 Test 开头或者以 Test 结尾。否则 surefire 不会自动执行。

    maven 项目目录布局:

    src
     |- main
     |- test
         |- kotlin   <--- 测试代码要写在这里
    pom.xml
    
  • TM12: kotest + FunSpec

    kotest 支持多种风格的测试用例定义 DSL,这里建议用 FunSpec 风格。

    Hint: FunSpec 是从 scala 生态中发展起来的

    测试代码的一般结构:

    // com.....test.TestMyService.kt
    
    class TestMyService : FunSpec({ 
    
        // 这里可以用来定义一些测试用到的变量、辅助函数等
    
        beforeSpec {
            // TestMyService 中所有测试开始之前,只运行一次,用作公共准备工作,比如启动一个内存数据库
        } 
    
        afterSpec {
            // TestMyService 所有测试完成之后,只运行一次,一般用来对 beforeSpec 的操作进行扫尾
        }
    
        beforeEach {
            // 每个测试用例之前都做的工作,比如恢复一些状态到预设值
        }
    
        afterEach {
            // 每个测试用例之后都做的工作
        }
    
        test("case A") {
            // 一个测试用例
        }
    
        test("case B") {
            // 另一个测试用例
        }
    
        ...
    })
    

    一个 FunSpec 中包含多个测试用例,每个测试用例都可以在 idea 中单独运行。无论一次运行一个还是多个用例,顺序都是:

    1. 执行整个 FunSpec 的 lambda 部分
    2. 执行 beforeSpec
    3. 对于(每个)要被执行的测试用例:
      1. 执行 beforeEach
      2. 执行 test 本体
      3. 执行 afterEach
    4. 执行 afterSpec

    Hint: 请确保用例之间都是独立的,一个用例的执行与否与另一个用例的成败无关

  • TM13: asserts

    • 依赖于 assert 而不是 println 来做测试。

    • 记住测试的目的不是调试。测试可以用来调试,但是测试不仅仅为了调试。用 println 第一次调试的时候很方便,但是后面程序修改之后,再也没有办法通过回归测试保持正确性了,不会有人每次运行用例都仔细把所有的输出都分析一遍。

      println 在调试的时候很有帮助,也不是禁止的。合理的顺序是:

         test(....) {
            // 运行要测的代码
            // 打印输出一些结果,可以不打印
            // 用 asserts 验证正确性,不可省略
         }
      

      常用的 asserts 包括:

      • assertTrue: 要求结果为真
      • assertFails: 要求 block 运行失败抛出异常
      • assertFalse: 和 assertTrue 相反
      • assertNull: 要求结果为空
      • assertNotNull: 要求结果非空
      • assertEquals: 相等
      • assertSame: 相同

      显然除了前 2 项都可以用 assertTrue 模拟,所以也不需要背什么。

      Hint: 除了 assertFails 的 block 内部,其他地方抛出未 try catch 的异常都等价于测试用例失败。

      Hint: assert 方法只用在测试中,正式代码里的条件校验,请使用 require 或者其他自定义方法。

  • TM14: 层叠式测试思路

    • 开发、测试底层,比如 DAO,utils
    • 开发、测试上一层依赖,比如内部 service
    • 再开发、测试更上一层依赖,比如上层 service
    • 再开发、测试最上面一层,比如 controller
    • 最后开发、测试整个配置集成

    在此过程中,测试的依赖项可以是真实的,也可以是 mock 的。到底是用真实对象还是 mock 对象,取决于开发者认为真实对象用起来是不是足够麻烦

  • TM15: 测试 DAO

    class MyDaoTest : FunSpec({
        // 偷懒一点,将来可以把 dao 换成正式的,不用重新写用例
        // 考究一点可以随意发挥编程技巧
        val dao = InMemoryMyDao
        
        test("test 1") {
             assertNotNull { dao.getOne(id) }
        }
    
        ...
    })
    
  • TM16: 从干净的数据库开始测试

  • 每个 DAO 测试用例都应该从干净的数据库开始测试。数据准备可以在用例范围内(beforeEach 块或者 test 块)进行。

    优先考虑 TestContainers 用于数据库测试。TestContainers 在测试期间启动一个docker 镜像来运行数据库,比内存数据库使用起来更简单也更容易模拟真实环境。

    class SomeTestWithMongo : FunSpec({
        val mongo = MongoDBContainer(DockerImageName.parse("mongo:4.0.10"))
        lateinit var db: MongoDatabase
    
        // 启动一个 MongoDB
        beforeSpec {
            mongo.start()
            mongo.replicaSetUrl
            val client = KMongo.createClient("mongodb://localhost:${mongo.getMappedPort(27017)}/?retryWrites=false")
            db = client.getDatabase("test")
        }
    
        // 测试结束后关闭 MongoDB
        afterSpec { mongo.stop() }
    }
    

    TestContainers 预置了丰富的数据库类型,还包括各种消息中间件、SaaS 等,具体请参考 官方文档

    不推荐运行独立的测试用数据库。这样做会导致测试依赖于环境,同时数据库中既有的数据会干扰测试的结果。

    Hint: 很多数据库问题只有数据量大了才能发现,然而这恐怕超出单元测试的范畴了 😦

  • TM17: 测试边界条件

    测试是用来发现问题的,测试用例不是用来说明程序能跑,而是用来 “考验” 程序有没有考虑不周的地方,比如说:

    • 输入有错误会怎么样?
    • 输入为空会怎么样?
    • unique 字段冲突会怎么样?
    • io 故障会怎么样?
    • 数字越界会怎么样?
    • 时间戳有没有更新?
    • 操作日志有没有记录?
    • 权限控制了吗?
    • 统计口径正确吗?
    • 有没有考虑统计字段为空的情况?
    • 过滤器的各种组合,以及没有过滤器的情况,处理正确吗?
    • 树编辑有没有循环?
  • TM18: API 和集成测试

  • TM19:维护测试用例

  • TM20: 为测试写注释/文档

2022-07-04

  • 道德评事 - 发起评事
    • 道德评判团成员获取
  • 道德评事 - 评事列表
  • 道德评事 - 评事列表 - 查看详情
  • 道德评事 - 评事列表 - 编辑
  • 道德评事 - 评事事项维护

2022-07-07

list.shuffled().take(3)	// 列表随机抽取3个

2022-07-11

阶级并非一个同质的完整实体,许多分析家会对资产阶级进行更细腻的划分。这些划分有:

  • 上层资产阶级(high bourgeoisie):由最富有的人所组成,包括了企业家、贸易家等等。

  • 中层资产阶级(middle bourgeoisie):高级金领或那些拥有固定继承财产或收入的人,他们比起上层资产阶级较不富有。

  • 小资产阶级或小布尔乔亚、小资阶级(petite bourgeoisie):高级白领或以小笔资金独立创业的人,他们可能雇佣少许的员工。

  • 而无产阶级这个词则是指那些剩下来最下层阶级的人(贫穷的劳工)

2022-07-22

  • Any(Object) 转 Document:先转成Json,再用Document.parse(json)

2022-07-28

  • 前端请求传Json对象则后端使用@RequestParam;

    前端请求传Json对象的字符串则后端使用@RequestBody。

docker pull harbor.sucsoft.com:30002/cowork/yunnan-organization:1.7.0

马云曾以一己之力宣战整个中国银行业,试图倒逼银行进行改革、颠覆畸形的发展模式,那时支付宝成为全球七大基金交易平台之一。他四处演讲,慷慨陈词:“我们不屑于吃民营企业的蛋糕,我们想吃那块国有企业中不作为的那部分人的蛋糕”,支付宝改变了整个中国的消费模式,显然它有更大的野心。

然而,却在即将上市的前三天,蚂蚁却被不可抗力因素釜底抽薪,在那之后,五个月内被国家三次约谈,立案调查,罚款百亿,阿里股价腰斩,Jack Ma自此销声匿迹。对此,国人无不称赞铁拳凌厉,拍手叫好。又一次,国家保护了他们的安全。

一边遏制本土科技企业野心的膨胀,一边批判科技企业只会抢农民饭碗,如同让一个壮士穿着裹脚布上战场,能够存活下去就已经是万幸了,却总得被嘲讽一波不思进取,拿来和国外科技企业做对比。

马斯克的SpaceX初期,从NASA挖掘了大量工程师。假设在中国,他的星链计划又能走到第几步,是否也会说出那句:“我想把支付宝送给祖国。”

Java8新特性

  • Interface

interface 的设计初衷是面向抽象,提高扩展性。这也留有一点遗憾,Interface 修改的时候,实现它的类也必须跟着改。

为了解决接口的修改与现有的实现不兼容的问题。新 interface 的方法可以用defaultstatic修饰,这样就可以有方法体,实现类也不必重写此方法。

一个 interface 中可以有多个方法被它们修饰,这 2 个修饰符的区别主要也是普通方法和静态方法的区别。

default 修饰的方法,是普通实例方法,可以用this调用,可以被子类继承、重写。

static 修饰的方法,使用上和一般类静态方法一样。但它不能被子类继承,只能用Interface调用。

public interface InterfaceNew {
    static void sm() {
        System.out.println("interface提供的方式实现");
    }
    static void sm2() {
        System.out.println("interface提供的方式实现");
    }

    default void def() {
        System.out.println("interface default方法");
    }
    default void def2() {
        System.out.println("interface default2方法");
    }
    //须要实现类重写
    void f();
}

public interface InterfaceNew1 {
    default void def() {
        System.out.println("InterfaceNew1 default方法");
    }
}

如果有一个类既实现了 InterfaceNew 接口又实现了 InterfaceNew1接口,它们都有def(),并且 InterfaceNew 接口和 InterfaceNew1接口没有继承关系的话,这时就必须重写def()。不然的话,编译的时候就会报错。

public class InterfaceNewImpl implements InterfaceNew , InterfaceNew1{
    public static void main(String[] args) {
        InterfaceNewImpl interfaceNew = new InterfaceNewImpl();
        interfaceNew.def();
    }

    @Override
    public void def() {
        InterfaceNew1.super.def();
    }

    @Override
    public void f() {
    }
}

在 Java 8 ,接口和抽象类的区别

很多小伙伴认为:“既然 interface 也可以有自己的方法实现,似乎和 abstract class 没多大区别了。”

其实它们还是有区别的

  1. interface 和 class 的区别,好像是废话,主要有:
    • 接口多实现,类单继承
    • 接口的方法是 public abstract 修饰,变量是 public static final 修饰。 abstract class 可以用其他修饰符
  2. interface 的方法是更像是一个扩展插件。而 abstract class 的方法是要继承的。

开始我们也提到,interface 新增defaultstatic修饰的方法,是为了解决接口的修改与现有的实现不兼容的问题,并不是为了要替代abstract class。在使用上,该用 abstract class 的地方还是要用 abstract class,不要因为 interface 的新特性而将之替换。

记住接口永远和类不一样。

TODO

  • gte、lte

  • Dubbo

  • 对项目工具类的熟悉,比如GenericBuilder是

  • Sequence序列

  • FeignClient

  • kafka

    //Iterable 执行顺序  举例:过滤长于三个字符的单词,并打印前四个单词的长度。
    val words = "The quick brown fox jumps over the lazy dog".split(" ")
    val lengthsList = words.filter { println("filter: $it"); it.length > 3 }
         .map { println("length: ${it.length}"); it.length }
         .take(4)
    
    println("Lengths of first 4 words longer than 3 chars:")
    println(lengthsList)、
    
    // 执行结果
    filter: The
    filter: quick
    filter: brown
    filter: fox
    filter: jumps
    filter: over
    filter: the
    filter: lazy
    filter: dog
    length: 5
    length: 5
    length: 5
    length: 4
    length: 4
    Lengths of first 4 words longer than 3 chars:
    [5, 5, 5, 4]
    
    // Sequence执行顺序  举例:过滤长于三个字符的单词,并打印前四个单词的长度。
    val originWords = "The quick brown fox jumps over the lazy dog".split(" ")
    // 将列表转换为序列
    val wordsSequence = originWords.asSequence()
    
    val lengthsSequence = wordsSequence.filter { println("filter: $it"); it.length > 3 }
          .map { println("length: ${it.length}"); it.length }
          .take(4)
    
    println("Lengths of first 4 words longer than 3 chars")
    // 末端操作:以列表形式获取结果。
    println(lengthsSequence.toList())
    
    // 执行结果
    Lengths of first 4 words longer than 3 chars
    filter: The
    filter: quick
    length: 5
    filter: brown
    length: 5
    filter: fox
    filter: jumps
    length: 5
    filter: over
    length: 4
    [5, 5, 5, 4]
    

  • 在浙大城院就读是什么体验?

2022届计算学院软工专业毕业,客观起见只对计算学院做评价;

先说一下个人感受:烂,剩下10天毕业典礼,只想赶紧领证跑路。

这是我的学分绩点图:

image-20220601112401446

软工专业这一届大概七十人不到,说下我目前了解到的情况:有两个阿里的,一个网易,一个港中深(后来似乎出国了),一个浙大软院。

看起来还行是不是?

现实是:阿里的两个纯靠个人能力,选修课必逃,必修课选逃,白天睡觉晚上刷题打游戏,游戏打到十二点开始刷题,半夜三点吃个外卖等天亮了上床睡觉,在我的印象里很少有早于三点睡觉的(没错我是牛马,我打游戏打到三点)。

出国的那个,因为

  • 最常见的模式:开个公司,利用职称拉资源拉项目,把研究生安排进自己公司做项目搞研究,出来的成果自然归自己,然后进一步评职称,做大做强。如果你问会不会coding、能不能给研究过程提供帮助,那很抱歉我狗屁不会,但如果论人情世故、论运营管理,那还是很绝的

posted @   小白酒菊  阅读(106)  评论(0编辑  收藏  举报
编辑推荐:
· 如何编写易于单元测试的代码
· 10年+ .NET Coder 心语,封装的思维:从隐藏、稳定开始理解其本质意义
· .NET Core 中如何实现缓存的预热?
· 从 HTTP 原因短语缺失研究 HTTP/2 和 HTTP/3 的设计差异
· AI与.NET技术实操系列:向量存储与相似性搜索在 .NET 中的实现
阅读排行:
· 10年+ .NET Coder 心语 ── 封装的思维:从隐藏、稳定开始理解其本质意义
· 地球OL攻略 —— 某应届生求职总结
· 提示词工程——AI应用必不可少的技术
· Open-Sora 2.0 重磅开源!
· 周边上新:园子的第一款马克杯温暖上架
点击右上角即可分享
微信分享提示