北冥有锅

北冥有锅 查看完整档案

填写现居城市  |  填写毕业院校  |  填写所在公司/组织填写个人主网站
编辑
_ | |__ _ _ __ _ | '_ \| | | |/ _` | | |_) | |_| | (_| | |_.__/ \__,_|\__, | |___/ 该用户太懒什么也没留下

个人动态

北冥有锅 赞了回答 · 5月29日

解决奇怪的Symbol的问题

这问题挺有意思的,我也从来没有注意到,翻了翻资料,发现原来是很多事情巧合的凑合到一块儿,然后出现了这个问题。准确的来说,是浏览器的默认行为和JavaScript的隐式类型变换捣的鬼。


一点一点来,首先,varlet的区别在哪里?

var声明的变量会被提升至当前函数作用域顶端,如果是在全局那么这个变量将会成为window的一个属性。
而对于let声明的变量,它会将变量提升至当前块级作用域,并且如果是在全局,当前变量也不会成为window的属性。

所以,在全局中会出现这样的事情:

var test1 = 'test1';
let test2 = 'test2';

console.log(window.test1);    // test1
console.log(window.test2);    // undefined

然后,name为名的变量和别的变量有什么区别?
上面我们知道了,var name = 'test1';实际上可以等同于window.name = 'test1',很容易就能想到,name是不是固定的保留字?

翻翻规范,还真是的。
window.name属性表示的是当前窗口上下文的名称。下面是window的部分接口:

[ReplaceableNamedProperties] 
interface Window {
  // the current browsing context
  readonly attribute WindowProxy window;
  readonly attribute WindowProxy self;
  readonly attribute Document document;
           attribute DOMString name;
  //..
}

name属性在这里的最后一行,没有readonly的前缀,说明它是可读可写的,它的数据类型则是DOMString
DOMString是指UTF-16的字符串,在JavaScript中它会直接映射到String

所以当我们给window.name赋值的时候,这个值会被强制转换为String

我们可以试试看:

var name = { a:1, b:2 };
console.log(window.name);    // [object Object]

var name = [0, 1, 2, 3];
console.log(window.name);    // 0,1,2,3

到了这里大概就能猜到,var name = Symbol('test');的错误,应该是Symbol变量在做类型转换的时候出了问题。而实际报的错误也证实了我们的猜测:TypeError: Cannot convert a Symbol value to a string

但是,似乎不太对,Symbol变量是可以转换成字符串的啊,比如:

let test = Symbol('test');

console.log(test.toString());    // Symbol(test)
console.log(String(test));       // Symbol(test)

嘛,这就是比较老生常谈的东西了,JavaScript的隐式类型变换和显式的强制转换对于部分变量是不同的。很不幸,在这里Symbol就是这么一类。

Symbol被隐式的转换时,它会首先调用其内部的ToPrimitive接口,拿到其原始值,然后在其中再调用ToString函数转换为字符串。注意,这里的这个ToString函数是其内部的抽象方法,和暴露在外的Symbol.prototype.toString()不是一个东西。

对于Symbol变量而言,当其调用ToString的时候就会报错,更详细的我就不展开了,有兴趣的可以自己看看规范:ToString ( argument )

关注 3 回答 4

北冥有锅 收藏了文章 · 5月28日

解决Vuex刷新重置,关闭窗口重置

Vuex结合sessionStorage存储公用的用户信息

前言

在一个系统中,我们经常会用到用户的用户名、用户手机号、用户ID等信息,可以将它们用于展示在系统中或者根据ID获取权限之类。

因为各个页面都需要这些公用信息,当然是存储在store的状态字中,登录之后就请求远程数据,赋值啦。

但是由于Vue是响应式的,我们存储在store中的状态字一旦刷新或者关闭窗口,网页都会初始化,重置,所以得想个方法动态更新store数据。

问题一:每次刷新页面store中数据都会重置

方法一:

我们需要知道,每次刷新加载一次页面,都会加载一次入口html文件(一般都是App.vue文件,页面间跳转是不会重新加载滴)。
所以可以将获取信息的接口的方法(GetPcMallInfo)放在App.vue中,一刷新就去请求一次接口,重新赋值store中的数据

<template>
    <div id="app">
        <router-view />
    </div>
</template>
<script>
    export default {
        name: 'App',
        created() {
            this.$store.dispatch('GetPcMallInfo', {}).then(() => {}).catch((e) => {})
        }
    }
</script>

缺点:远程请求数据,如果网络延迟,那么store中的数据更新也会出现延迟的情况,那么展示在页面中的用户名之类的数据也会延迟渲染,非常影响客户体验;如果数据量过大,频繁的请求数据也是一种性能损耗。

方法二:

  1. 首先,当然是在登录之后,先请求接口,把公用数据存储在store中,
  2. 其次,在页面刷之新,将store数据存储在sessionStorage中。
    当然还是是还是写在入口html,App.vue文件中啦。

    window.addEventListener('beforeunload', () => {
        sessionStorage.setItem('store', JSON.stringify(this.$store.state))
    })
  3. 最后,刷新后,将sessionStorage中的值赋值给store。由于sessionStorage是本地存储,不需要远程请求,就不会出现延迟的情况啦。

    if (sessionStorage.getItem('store')) {
        this.$store.replaceState(Object.assign({}, this.$store.state, JSON.parse(sessionStorage.getItem('store'))))
    }

问题二:使用方法二时,关闭窗口,sessionStorage会被清空

这时,虽然关闭了窗口,但是没有关闭浏览器,重新打开网页,还是处于登录状态,但是sessionStorage会清空,那么store中的状态也没了。

这里也有两个方法解决:

  1. 不用sessionStorage存储store的值,用localStorage。关闭当前窗口或浏览器可不会清空localStorage。但是,每次打开网页会保留上一次登录的公共信息。比如上一次是用户A登录,用户B能在localStorage中看到用户A的信息。
  2. 如果有登录信息,但是sessionStorage的值是空的,重新请求GetPcMallInfo方法一次呗。还想什么呢!

终极版

  1. localStorage

    <template>
        <div id="app">
            <router-view />
        </div>
    </template>
    <script>
        // 入口组件
        export default {
            name: 'App',
            created() {
                if (window.localStorage.getItem('store')) {
                    this.$store.replaceState(Object.assign({}, this.$store.state, JSON.parse(window.localStorage.getItem('store'))))
                }
                window.addEventListener('beforeunload', () => {
                    window.localStorage.setItem('store', JSON.stringify(this.$store.state))
                })
            },
        }
    </script>
  2. 重新请求

    <template>
        <div id="app">
            <router-view />
        </div>
    </template>
    <script>
    import {getToken} from '@/utils/auth' 
        // 入口组件
        export default {
            name: 'App',
            created() {
    
                if (sessionStorage.getItem('store')) {
                    this.$store.replaceState(Object.assign({}, this.$store.state, JSON.parse(sessionStorage.getItem('store'))))
                }
                window.addEventListener('beforeunload', () => {
                    sessionStorage.setItem('store', JSON.stringify(this.$store.state))
                })
                
                if(this.$store.getters.MallName==''&&getToken()){
                    this.$store.dispatch('GetPcMallInfo', {}).then(() => {}).catch((e) => {})
                }
            },
        }
    </script>

问题三:关闭浏览器了,怎么办

这。。。。。。。不要问

今天的你学废了么
查看原文

北冥有锅 发布了文章 · 5月28日

解决Vuex刷新重置,关闭窗口重置

Vuex结合sessionStorage存储公用的用户信息

前言

在一个系统中,我们经常会用到用户的用户名、用户手机号、用户ID等信息,可以将它们用于展示在系统中或者根据ID获取权限之类。

因为各个页面都需要这些公用信息,当然是存储在store的状态字中,登录之后就请求远程数据,赋值啦。

但是由于Vue是响应式的,我们存储在store中的状态字一旦刷新或者关闭窗口,网页都会初始化,重置,所以得想个方法动态更新store数据。

问题一:每次刷新页面store中数据都会重置

方法一:

我们需要知道,每次刷新加载一次页面,都会加载一次入口html文件(一般都是App.vue文件,页面间跳转是不会重新加载滴)。
所以可以将获取信息的接口的方法(GetPcMallInfo)放在App.vue中,一刷新就去请求一次接口,重新赋值store中的数据

<template>
    <div id="app">
        <router-view />
    </div>
</template>
<script>
    export default {
        name: 'App',
        created() {
            this.$store.dispatch('GetPcMallInfo', {}).then(() => {}).catch((e) => {})
        }
    }
</script>

缺点:远程请求数据,如果网络延迟,那么store中的数据更新也会出现延迟的情况,那么展示在页面中的用户名之类的数据也会延迟渲染,非常影响客户体验;如果数据量过大,频繁的请求数据也是一种性能损耗。

方法二:

  1. 首先,当然是在登录之后,先请求接口,把公用数据存储在store中,
  2. 其次,在页面刷之新,将store数据存储在sessionStorage中。
    当然还是是还是写在入口html,App.vue文件中啦。

    window.addEventListener('beforeunload', () => {
        sessionStorage.setItem('store', JSON.stringify(this.$store.state))
    })
  3. 最后,刷新后,将sessionStorage中的值赋值给store。由于sessionStorage是本地存储,不需要远程请求,就不会出现延迟的情况啦。

    if (sessionStorage.getItem('store')) {
        this.$store.replaceState(Object.assign({}, this.$store.state, JSON.parse(sessionStorage.getItem('store'))))
    }

问题二:使用方法二时,关闭窗口,sessionStorage会被清空

这时,虽然关闭了窗口,但是没有关闭浏览器,重新打开网页,还是处于登录状态,但是sessionStorage会清空,那么store中的状态也没了。

这里也有两个方法解决:

  1. 不用sessionStorage存储store的值,用localStorage。关闭当前窗口或浏览器可不会清空localStorage。但是,每次打开网页会保留上一次登录的公共信息。比如上一次是用户A登录,用户B能在localStorage中看到用户A的信息。
  2. 如果有登录信息,但是sessionStorage的值是空的,重新请求GetPcMallInfo方法一次呗。还想什么呢!

终极版

  1. localStorage

    <template>
        <div id="app">
            <router-view />
        </div>
    </template>
    <script>
        // 入口组件
        export default {
            name: 'App',
            created() {
                if (window.localStorage.getItem('store')) {
                    this.$store.replaceState(Object.assign({}, this.$store.state, JSON.parse(window.localStorage.getItem('store'))))
                }
                window.addEventListener('beforeunload', () => {
                    window.localStorage.setItem('store', JSON.stringify(this.$store.state))
                })
            },
        }
    </script>
  2. 重新请求

    <template>
        <div id="app">
            <router-view />
        </div>
    </template>
    <script>
    import {getToken} from '@/utils/auth' 
        // 入口组件
        export default {
            name: 'App',
            created() {
    
                if (sessionStorage.getItem('store')) {
                    this.$store.replaceState(Object.assign({}, this.$store.state, JSON.parse(sessionStorage.getItem('store'))))
                }
                window.addEventListener('beforeunload', () => {
                    sessionStorage.setItem('store', JSON.stringify(this.$store.state))
                })
                
                if(this.$store.getters.MallName==''&&getToken()){
                    this.$store.dispatch('GetPcMallInfo', {}).then(() => {}).catch((e) => {})
                }
            },
        }
    </script>

问题三:关闭浏览器了,怎么办

这。。。。。。。不要问

今天的你学废了么
查看原文

赞 3 收藏 2 评论 0

北冥有锅 关注了专栏 · 5月27日

别再问我系列

别再问了,静静看就行。

关注 234

北冥有锅 赞了回答 · 5月25日

事件冒泡和事件捕获有怎么样应用场景,又如何阻止呢?

比如在一个HTML结构中,父级包含着子级,当事件在子级发生时(click,mouseenter,mouseleave等),由于事件冒泡就会触发父级的同名事件。示例:

<ul id="parent">
    <li id="child">son-1</li>
</ul>
    
   child.addEventListener("click",function(ev){
        alert(1);
        //ev.stopPropagation();标准浏览器
        //或者是
        //ev.cancelBubble = true; //IE
    })
    parent.addEventListener("click",function(ev){
        alert(1);
    })

当点击li时,会弹出两个1,可以通过阻止冒泡防止这样的行为,点击li就弹出一个1。
ev.stopPropagation()或ev.cancelBubble = true 可以阻止事件冒泡。

关注 3 回答 2

北冥有锅 收藏了问题 · 5月24日

java.lang.NoClassDefFoundError: 一个关于jackson的jar问题..

问题出现的环境背景及自己尝试过哪些方法

java.lang.NoClassDefFoundError:com/fasterxml/jackson/databind/ObjectMapper
请问这个问题怎么解决呢??????/
QQ截图20200522165414.jpg

我用debug,执行完就会出现这一句找不到class的异常,但是在导入的jar包里找得到这个ObjectMapper...

QQ截图20200522165445.jpg
这个是相关jar

本来导入的是jackson-databind-2.2.3.jar ,一直网上搜问题,有说版本问题的,然后就换了,最后还是不行,都是这个异常### 问题描述

相关代码

package cn.wei.web.servlet;


import com.fasterxml.jackson.databind.ObjectMapper;

import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;

public class FindUserServlet extends HttpServlet {
    protected void doPost(HttpServletRequest request, HttpServletResponse response)
            throws ServletException, IOException {
        ObjectMapper mapper = new ObjectMapper();
        System.out.println("ni shi tiancai ");
        //1.获取用户名
        String username = request.getParameter("username");

        //2.调用service层判断用户名是否存在

        //期望服务器响应回的数据格式:{"userExsit":true,"msg":"此用户名太受欢迎,请更换一个"}
        //                         {"userExsit":false,"msg":"用户名可用"}

        //设置响应的数据格式为json
        response.setContentType("application/json;charset=utf-8");
        Map<String,Object> jsons = new HashMap<>();
        if("tom".equals(username)){
            //存在
            jsons.put("userExsit",true);
            jsons.put("msg","此用户名太受欢迎,请更换一个");
        }else{
            //不存在
            jsons.put("userExsit",false);
            jsons.put("msg","用户名可用");
        }



        //并且传递给客户端
        mapper.writeValue(response.getWriter(),jsons);


    }

    protected void doGet(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException {
        this.doPost(request, response);
    }
}

你期待的结果是什么?实际看到的错误信息又是什么?

希望能运行成功..但是这个类不知道为什么找不到

北冥有锅 收藏了文章 · 5月24日

「JVM」原始类型 boolean 在 JVM 中的讨论

导言

在开始学习JVM字节码之后,遇到了一个有意思的问题,下面这段代码,会输出什么:

public class Foo {
    public static void main(String[] args) {
        boolean flag = true;
        if (flag) {
            System.out.print("A");
        }
        if (flag == true) {
            System.out.print("B");
        }
    }
}

这个问题的答案很显然——会输出AB

接下来重点来了,如果将 2 赋值给flag变量,会输出什么呢?如果将flag赋值为 3 呢?

要知道这两个问题的答案,我们得知道在JVMboolean类型的变量是如何表示的,以及在这两个if语句中到底进行了怎样的判断。

boolean 类型在 JVM 中是如何表示的

在命令行中输入javap -c Foo,得到反编译的字节码如下:

Compiled from "Foo.java"
public class geektime.part1.Foo {
  public geektime.part1.Foo();
    Code:
       0: aload_0
       1: invokespecial #1                  // Method java/lang/Object."<init>":()V
       4: return

  public static void main(java.lang.String[]);
    Code:
       0: iconst_1
       1: istore_1
       2: iload_1
       3: ifeq          14
       6: getstatic     #2                  // Field java/lang/System.out:Ljava/io/PrintStream;
       9: ldc           #3                  // String Hello, Java!
      11: invokevirtual #4                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
      14: iload_1
      15: iconst_1
      16: if_icmpne     27
      19: getstatic     #2                  // Field java/lang/System.out:Ljava/io/PrintStream;
      22: ldc           #5                  // String Hello, JVM!
      24: invokevirtual #4                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
      27: return
}

在字节码文件的main方法中,第0~2行完成了将一个int类型的常量赋值给了第一个变量flag的操作。于是我们得到了结论:

  • JVMboolean类型的变量是用数字 0 和 1 来表示的。false用 0 表示,true用 1 表示

if (flag) 分析

然后继续看字节码文件,接下来的字节码就是代表第一个判断语句if (flag) 语句,从3~11行都属于第一个if代码块,首先是ifeq

7A29389B-DD73-4675-9DF2-B36FB06BD56E.png

ifeq助记符的作用是:当栈顶 int 型数值等于0时跳转。这里跳转的意思就是不执行语句块中的代码。

此时的栈顶 int 型数值就是刚刚被赋值给 flag 的1,所以在这里ifeq 14的意思就是当 flag 等于 0 的时候跳转到14行,由于第14已经不属于 if 语句的范围了,所以这里的跳转是不执行 if 语句的意思。也就是说,if(flag)中是判断 flag 的值,当 flag 值不等于0的时候才执行 if 中的语句

if (flag == true) 分析

接下来继续看第二个判断语句if (flag == true)语句,是在字节码的14~24行。前面两行是将一个int类型的数值1和flag变量推送到栈顶,可以理解为把接下来将要进行比较的true放入将要进行比较的一个“容器”,然后是if_icmpne助记符。

7A29389B-DD73-4675-9DF2-B36FB06BD56E.png

if_icmpne助记符的作用是:比较栈顶两int型数值大小,当结果不相等时跳转。

现在栈顶两int型的数值是刚刚推送的true也就是1,以及flag变量,所以if_icmpne助记符是比较这两个数值,如果他们相等,执行if语句的内容。也就是说,if(flag == true)中是进行flagtrue的判断,当它们相等时执行if中的语句

为什么编译器提示可以简化

关于上述的示例代码,编译器或许会在第二个if语句提醒你可以Simplify,当你执行简化以后你会发现它将if (flag == true)变成了if (flag),如下图所示。

559A0D7D-7954-4A7E-8D64-EC6A352F7AD1.png

在没有Simplify之前,这两个 if 语句如果要执行,第一个的条件是 flag 不等0,第二个的条件是 flag 等于1。这是两个完全不同的比较嘛!但是为什么在编译器中可以画上等号呢?

timg.jpeg

当我们已经了解过boolean类型的变量在虚拟机中的表现形式之后,答案显而易见。首先这里的flag是一个boolean类型的变量,它只有truefalse两种值,即在虚拟机中只有0和1两种值。所以对于一个boolean类型的变量,不等0就代表了它一定等1,所以编译器才会发出可以简化的提示。

如果将 flag 的值赋为其他整数型值

我们知道在正常情况下编译器不会接受将2这一个数字赋值给boolean类型变量的这么一个操作,但是我们可以通过一些其他的工具(如asmtools)来实现这个操作。

  1. flag的值赋为2。在字节码中就是将iconst_2赋值给flag时,输出为空。
  2. flag的值赋为3。在字节码中就是将iconst_3赋值给flag时,输出为AB
  3. 如果再多做几个实验,将flag的值赋为4,输出为空;将flag的值赋为5,输出为AB......

如果我们将这些整数都转化为二进制,即2=0010,3=0011,4=0100,5=0101。
当二进制末尾为0时,无输出,当二进制末尾为1时,输出AB

由此我们可以得出结论:如果将其他整数类型的值赋值给一个boolean类型的变量,虚拟机会取此整数值二进制的最后一位。

总结

  1. boolean类型的变量在虚拟机中有两种表现形式:true用1表示,false用0表示。
  2. 当虚拟机执行if(boolean)的条件判断时,实际进行的判断是该布尔值是否不等于0,当该布尔值不等于0时执行if语句中的内容。
  3. 当虚拟机执行if(boolean==true)的条件判断时,实际进行的判断时该布尔值是否等于1,当该布尔值等于1时执行if语句中的内容。
  4. 编译器提示if(boolean==true)可以简化为if(boolean)的原因是在虚拟机中布尔值只有两种值,执行不等于0的判断与执行等于1的判断是等价的。
  5. 若在虚拟机中将其他int值赋值给一个布尔类型的变量,虚拟机会取此整数值二进制的最后一位。
查看原文

北冥有锅 赞了文章 · 5月24日

「JVM」原始类型 boolean 在 JVM 中的讨论

导言

在开始学习JVM字节码之后,遇到了一个有意思的问题,下面这段代码,会输出什么:

public class Foo {
    public static void main(String[] args) {
        boolean flag = true;
        if (flag) {
            System.out.print("A");
        }
        if (flag == true) {
            System.out.print("B");
        }
    }
}

这个问题的答案很显然——会输出AB

接下来重点来了,如果将 2 赋值给flag变量,会输出什么呢?如果将flag赋值为 3 呢?

要知道这两个问题的答案,我们得知道在JVMboolean类型的变量是如何表示的,以及在这两个if语句中到底进行了怎样的判断。

boolean 类型在 JVM 中是如何表示的

在命令行中输入javap -c Foo,得到反编译的字节码如下:

Compiled from "Foo.java"
public class geektime.part1.Foo {
  public geektime.part1.Foo();
    Code:
       0: aload_0
       1: invokespecial #1                  // Method java/lang/Object."<init>":()V
       4: return

  public static void main(java.lang.String[]);
    Code:
       0: iconst_1
       1: istore_1
       2: iload_1
       3: ifeq          14
       6: getstatic     #2                  // Field java/lang/System.out:Ljava/io/PrintStream;
       9: ldc           #3                  // String Hello, Java!
      11: invokevirtual #4                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
      14: iload_1
      15: iconst_1
      16: if_icmpne     27
      19: getstatic     #2                  // Field java/lang/System.out:Ljava/io/PrintStream;
      22: ldc           #5                  // String Hello, JVM!
      24: invokevirtual #4                  // Method java/io/PrintStream.println:(Ljava/lang/String;)V
      27: return
}

在字节码文件的main方法中,第0~2行完成了将一个int类型的常量赋值给了第一个变量flag的操作。于是我们得到了结论:

  • JVMboolean类型的变量是用数字 0 和 1 来表示的。false用 0 表示,true用 1 表示

if (flag) 分析

然后继续看字节码文件,接下来的字节码就是代表第一个判断语句if (flag) 语句,从3~11行都属于第一个if代码块,首先是ifeq

7A29389B-DD73-4675-9DF2-B36FB06BD56E.png

ifeq助记符的作用是:当栈顶 int 型数值等于0时跳转。这里跳转的意思就是不执行语句块中的代码。

此时的栈顶 int 型数值就是刚刚被赋值给 flag 的1,所以在这里ifeq 14的意思就是当 flag 等于 0 的时候跳转到14行,由于第14已经不属于 if 语句的范围了,所以这里的跳转是不执行 if 语句的意思。也就是说,if(flag)中是判断 flag 的值,当 flag 值不等于0的时候才执行 if 中的语句

if (flag == true) 分析

接下来继续看第二个判断语句if (flag == true)语句,是在字节码的14~24行。前面两行是将一个int类型的数值1和flag变量推送到栈顶,可以理解为把接下来将要进行比较的true放入将要进行比较的一个“容器”,然后是if_icmpne助记符。

7A29389B-DD73-4675-9DF2-B36FB06BD56E.png

if_icmpne助记符的作用是:比较栈顶两int型数值大小,当结果不相等时跳转。

现在栈顶两int型的数值是刚刚推送的true也就是1,以及flag变量,所以if_icmpne助记符是比较这两个数值,如果他们相等,执行if语句的内容。也就是说,if(flag == true)中是进行flagtrue的判断,当它们相等时执行if中的语句

为什么编译器提示可以简化

关于上述的示例代码,编译器或许会在第二个if语句提醒你可以Simplify,当你执行简化以后你会发现它将if (flag == true)变成了if (flag),如下图所示。

559A0D7D-7954-4A7E-8D64-EC6A352F7AD1.png

在没有Simplify之前,这两个 if 语句如果要执行,第一个的条件是 flag 不等0,第二个的条件是 flag 等于1。这是两个完全不同的比较嘛!但是为什么在编译器中可以画上等号呢?

timg.jpeg

当我们已经了解过boolean类型的变量在虚拟机中的表现形式之后,答案显而易见。首先这里的flag是一个boolean类型的变量,它只有truefalse两种值,即在虚拟机中只有0和1两种值。所以对于一个boolean类型的变量,不等0就代表了它一定等1,所以编译器才会发出可以简化的提示。

如果将 flag 的值赋为其他整数型值

我们知道在正常情况下编译器不会接受将2这一个数字赋值给boolean类型变量的这么一个操作,但是我们可以通过一些其他的工具(如asmtools)来实现这个操作。

  1. flag的值赋为2。在字节码中就是将iconst_2赋值给flag时,输出为空。
  2. flag的值赋为3。在字节码中就是将iconst_3赋值给flag时,输出为AB
  3. 如果再多做几个实验,将flag的值赋为4,输出为空;将flag的值赋为5,输出为AB......

如果我们将这些整数都转化为二进制,即2=0010,3=0011,4=0100,5=0101。
当二进制末尾为0时,无输出,当二进制末尾为1时,输出AB

由此我们可以得出结论:如果将其他整数类型的值赋值给一个boolean类型的变量,虚拟机会取此整数值二进制的最后一位。

总结

  1. boolean类型的变量在虚拟机中有两种表现形式:true用1表示,false用0表示。
  2. 当虚拟机执行if(boolean)的条件判断时,实际进行的判断是该布尔值是否不等于0,当该布尔值不等于0时执行if语句中的内容。
  3. 当虚拟机执行if(boolean==true)的条件判断时,实际进行的判断时该布尔值是否等于1,当该布尔值等于1时执行if语句中的内容。
  4. 编译器提示if(boolean==true)可以简化为if(boolean)的原因是在虚拟机中布尔值只有两种值,执行不等于0的判断与执行等于1的判断是等价的。
  5. 若在虚拟机中将其他int值赋值给一个布尔类型的变量,虚拟机会取此整数值二进制的最后一位。
查看原文

赞 4 收藏 2 评论 0

北冥有锅 收藏了文章 · 5月24日

「MySQL」更新语句 update 踩坑日记

背景

最近在一次线上作业过程中执行了一句DML语句,本以为万无一失,结果应用反馈说没有更新,数据还是没有变,最后经过排查才发现是我语句写错了,导致update语句执行的结果与预期不符。

情景再现

为了方便演示,建立一张用户表,同时插入五条数据。

create table user(
id int(12) comment '用户主键id',
name varchar(36) comment '用户名',
age int(12) comment '年龄');

insert into user values (1,'one',11),(2,'two',12),(3,'three',13),(4,'four',15),(5,'five',15);

执行完成后,现在user表中的数据如下:

+------+-------+------+
| id   | name  | age  |
+------+-------+------+
|    1 | one   |   11 |
|    2 | two   |   12 |
|    3 | three |   13 |
|    4 | four  |   15 |
|    5 | five  |   15 |
+------+-------+------+

现在需要把所有的年龄改成 10、用户名改成user——假设此操作有意义——我提交到运维的 DML 语句如下:

update user set age=10 and name='user';

当我刷新用户表,看到执行update语句后的表全部数据如下:

+------+-------+------+
| id   | name  | age  |
+------+-------+------+
|    1 | one   |    0 |
|    2 | two   |    0 |
|    3 | three |    0 |
|    4 | four  |    0 |
|    5 | five  |    0 |
+------+-------+------+

神奇的事情发生了,age字段全部被更新成0,而name字段竟然没有任何修改!

错误原因及修正

错误原因其实很简单,update语句写错了。MySQLupdate语句的语法是

UPDATE table_name
SET column1=value1,column2=value2,...
WHERE some_column=some_value;

如果更新多个字段,相邻字段间应该以逗号分隔而不是and。如果 update 语句使用 and 作为多个字段之间的分隔符,就像最开始我提交给运维的那样,这个更新语句最终将会变成

update user set age=(10 and name='user');

(10 and name='user')作为一个返回值为 boolean 类型的判断语句,返回会被映射成 1 或 0,有 99.999% 的可能会让第一个更新变量更新为错误的数据。

正确的更新语句应该是

update user set age=10, name='user';

教训

  1. 在提交DML语句前先在测试环境试一下
  2. 基础的SQL语法不要记错

u=1739048474,1791924378&fm=26&gp=0.jpg

小结

  1. MySQLupdate语句的语法是
UPDATE table_name
SET column1=value1,column2=value2,...
WHERE some_column=some_value;
查看原文

北冥有锅 收藏了文章 · 5月24日

「MySQL」更新语句 update 踩坑日记

背景

最近在一次线上作业过程中执行了一句DML语句,本以为万无一失,结果应用反馈说没有更新,数据还是没有变,最后经过排查才发现是我语句写错了,导致update语句执行的结果与预期不符。

情景再现

为了方便演示,建立一张用户表,同时插入五条数据。

create table user(
id int(12) comment '用户主键id',
name varchar(36) comment '用户名',
age int(12) comment '年龄');

insert into user values (1,'one',11),(2,'two',12),(3,'three',13),(4,'four',15),(5,'five',15);

执行完成后,现在user表中的数据如下:

+------+-------+------+
| id   | name  | age  |
+------+-------+------+
|    1 | one   |   11 |
|    2 | two   |   12 |
|    3 | three |   13 |
|    4 | four  |   15 |
|    5 | five  |   15 |
+------+-------+------+

现在需要把所有的年龄改成 10、用户名改成user——假设此操作有意义——我提交到运维的 DML 语句如下:

update user set age=10 and name='user';

当我刷新用户表,看到执行update语句后的表全部数据如下:

+------+-------+------+
| id   | name  | age  |
+------+-------+------+
|    1 | one   |    0 |
|    2 | two   |    0 |
|    3 | three |    0 |
|    4 | four  |    0 |
|    5 | five  |    0 |
+------+-------+------+

神奇的事情发生了,age字段全部被更新成0,而name字段竟然没有任何修改!

错误原因及修正

错误原因其实很简单,update语句写错了。MySQLupdate语句的语法是

UPDATE table_name
SET column1=value1,column2=value2,...
WHERE some_column=some_value;

如果更新多个字段,相邻字段间应该以逗号分隔而不是and。如果 update 语句使用 and 作为多个字段之间的分隔符,就像最开始我提交给运维的那样,这个更新语句最终将会变成

update user set age=(10 and name='user');

(10 and name='user')作为一个返回值为 boolean 类型的判断语句,返回会被映射成 1 或 0,有 99.999% 的可能会让第一个更新变量更新为错误的数据。

正确的更新语句应该是

update user set age=10, name='user';

教训

  1. 在提交DML语句前先在测试环境试一下
  2. 基础的SQL语法不要记错

u=1739048474,1791924378&fm=26&gp=0.jpg

小结

  1. MySQLupdate语句的语法是
UPDATE table_name
SET column1=value1,column2=value2,...
WHERE some_column=some_value;
查看原文

认证与成就

  • 获得 2 次点赞
  • 获得 2 枚徽章 获得 0 枚金徽章, 获得 0 枚银徽章, 获得 2 枚铜徽章

擅长技能
编辑

(゚∀゚ )
暂时没有

开源项目 & 著作
编辑

(゚∀゚ )
暂时没有

注册于 5月23日
个人主页被 77 人浏览