什么是语法糖?Java中有哪些语法糖?

什么是语法糖?Java中有哪些语法糖?

语法糖

  • 语法糖(Syntactic Sugar),也称糖衣语法,是由英国计算机学家 Peter.J.Landin 发明的一个术语,指在计算机语言中添加的某种语法,这种语法对语言的功能并没有影响,但是更方便程序员使用。简而言之,语法糖让程序更加简洁,有更高的可读性。
  • 有意思的是,在编程领域,除了语法糖,还有语法盐和语法糖精的说法,篇幅有限这里不做扩展了。
  • 我们所熟知的编程语言中几乎都有语法糖。作者认为,语法糖的多少是评判一个语言够不够牛逼的标准之一。
  • 很多人说Java是一个“低糖语言”,其实从Java 7开始Java语言层面上一直在添加各种糖,主要是在“Project Coin”项目下研发。尽管现在Java有人还是认为现在的Java是低糖,未来还会持续向着“高糖”的方向发展。

解语法糖

  • 前面提到过,语法糖的存在主要是方便开发人员使用。但其实,Java虚拟机并不支持这些语法糖。这些语法糖在编译阶段就会被还原成简单的基础语法结构,这个过程就是解语法糖。
  • 说到编译,大家肯定都知道,Java语言中,javac命令可以将后缀名为.java的源文件编译为后缀名为.class的可以运行于Java虚拟机的字节码。
  • 如果你去看com.sun.tools.javac.main.JavaCompiler的源码,你会发现在compile()中有一个步骤就是调用desugar(),这个方法就是负责解语法糖的实现的。
  • Java 中最常用的语法糖主要有泛型、变长参数、条件编译、自动拆装箱、内部类等。本文主要来分析下这些语法糖背后的原理。一步一步剥去糖衣,看看其本质。

糖块介绍

switch 支持 String 与枚举

  • 前面提到过,从Java 7 开始,Java语言中的语法糖在逐渐丰富,其中一个比较重要的就是Java 7中switch开始支持String。

  • 在开始coding之前先科普下,Java中的swith自身原本就支持基本类型。比如int、char等。

  • 对于int类型,直接进行数值的比较。对于char类型则是比较其ascii码。

  • 所以,对于编译器来说,switch中其实只能使用整型,任何类型的比较都要转换成整型。比如byte。short,char(ackii码是整型)以及int。

  • 那么接下来看下switch对String得支持,有以下代码:

  • public class switchDemoString {public static void main(String[] args) {String str = "world";switch (str) {case "hello":System.out.println("hello");break;case "world":System.out.println("world");break;default:break;}}
    }
    
  • 反编译后内容如下:

  • public class switchDemoString
    {public switchDemoString(){}public static void main(String args[]){String str = "world";String s;switch((s = str).hashCode()){default:break;case 99162322:if(s.equals("hello"))System.out.println("hello");break;case 113318802:if(s.equals("world"))System.out.println("world");break;}}
    }
    
  • 看到这个代码,你知道原来字符串的switch是通过equals()和hashCode()方法来实现的。还好hashCode()方法返回的是int,而不是long。

  • 仔细看下可以发现,进行switch的实际是哈希值,然后通过使用equals方法比较进行安全检查,这个检查是必要的,因为哈希可能会发生碰撞。因此它的性能是不如使用枚举进行switch或者使用纯整数常量,但这也不是很差。

泛型

  • 我们都知道,很多语言都是支持泛型的,但是很多人不知道的是,不同的编译器对于泛型的处理方式是不同的。

  • 通常情况下,一个编译器处理泛型有两种方式:Code specialization和Code sharing。

  • C++和C#是使用Code specialization的处理机制,而Java使用的是Code sharing的机制。

  • Code sharing方式为每个泛型类型创建唯一的字节码表示,并且将该泛型类型的实例都映射到这个唯一的字节码表示上。将多种泛型类形实例映射到唯一的字节码表示是通过类型擦除(type erasue)实现的。

  • 也就是说,对于Java虚拟机来说,他根本不认识Map<String, String> map这样的语法。需要在编译阶段通过类型擦除的方式进行解语法糖。

  • 类型擦除的主要过程如下:

    • 将所有的泛型参数用其最左边界(最顶级的父类型)类型替换。
    • 移除所有的类型参数。
  • 以下代码:

  • Map<String, String> map = new HashMap<String, String>();  
    map.put("name", "h");  
    map.put("wechat", "s");  
    map.put("blog", "m");  
    
  • 解语法糖之后会变成:

  • Map map = new HashMap();  
    map.put("name", "h");  
    map.put("wechat", "s");  
    map.put("blog", "m");  
    
  • 以下代码:

  • public static <A extends Comparable<A>> A max(Collection<A> xs) {Iterator<A> xi = xs.iterator();A w = xi.next();while (xi.hasNext()) {A x = xi.next();if (w.compareTo(x) < 0)w = x;}return w;
    }
    
  • 类型擦除后会变成:

  •  public static Comparable max(Collection xs){Iterator xi = xs.iterator();Comparable w = (Comparable)xi.next();while(xi.hasNext()){Comparable x = (Comparable)xi.next();if(w.compareTo(x) < 0)w = x;}return w;
    }
    
  • 虚拟机中没有泛型,只有普通类和普通方法,所有泛型类的类型参数在编译时都会被擦除,泛型类并没有自己独有的Class类对象。比如并不存在List.class或是List.class,而只有List.class。

自动装箱与拆箱

  • 自动装箱就是Java自动将原始类型值转换成对应的对象,比如将int的变量转换成Integer对象,这个过程叫做装箱,反之将Integer对象转换成int类型值,这个过程叫做拆箱。

  • 因为这里的装箱和拆箱是自动进行的非人为转换,所以就称作为自动装箱和拆箱。

  • 原始类型byte, short, char, int, long, float, double 和 boolean 对应的封装类为Byte, Short, Character, Integer, Long, Float, Double, Boolean。

  • 先来看个自动装箱的代码:

  • public static void main(String[] args) {int i = 10;Integer n = i;
    }
    
  • 反编译后代码如下:

  • public static void main(String args[])
    {int i = 10;Integer n = Integer.valueOf(i);
    }
    
  • 再来看个自动拆箱的代码:

  • public static void main(String[] args) {Integer i = 10;int n = i;
    }
    
  • 反编译后代码如下:

  • public static void main(String args[])
    {Integer i = Integer.valueOf(10);int n = i.intValue();
    }
    
  • 从反编译得到内容可以看出,在装箱的时候自动调用的是Integer的valueOf(int)方法。而在拆箱的时候自动调用的是Integer的intValue方法。

  • 所以,装箱过程是通过调用包装器的valueOf方法实现的,而拆箱过程是通过调用包装器的 xxxValue方法实现的。

方法变长参数

  • 可变参数(variable arguments)是在Java 1.5中引入的一个特性。它允许一个方法把任意数量的值作为参数。

  • 看下以下可变参数代码,其中print方法接收可变参数:

  • public static void main(String[] args){print("");}public static void print(String... strs)
    {for (int i = 0; i < strs.length; i++){System.out.println(strs[i]);}
    }
    
  • 反编译后代码:

  • public static void main(String args[])
    {print(new String[] {""});
    }// transient 不能修饰方法,这里应该是反编译错误了?
    public static transient void print(String strs[])
    {for(int i = 0; i < strs.length; i++)System.out.println(strs[i]);}
    
  • 从反编译后代码可以看出,可变参数在被使用的时候,他首先会创建一个数组,数组的长度就是调用该方法是传递的实参的个数,然后再把参数值全部放到这个数组当中,然后再把这个数组作为参数传递到被调用的方法中。

枚举

  • Java SE5提供了一种新的类型-Java的枚举类型,关键字enum可以将一组具名的值的有限集合创建为一种新的类型,而这些具名的值可以作为常规的程序组件使用,这是一种非常有用的功能。

  • 要想看源码,首先得有一个类吧,那么枚举类型到底是什么类呢?是enum吗?

  • 答案很明显不是,enum就和class一样,只是一个关键字,他并不是一个类。

  • 那么枚举是由什么类维护的呢,我们简单的写一个枚举:

  • public enum t {SPRING,SUMMER;
    }
    
  • 然后我们使用反编译,看看这段代码到底是怎么实现的,反编译后代码内容如下:

  • public final class T extends Enum
    {private T(String s, int i){super(s, i);}public static T[] values(){T at[];int i;T at1[];System.arraycopy(at = ENUM$VALUES, 0, at1 = new T[i = at.length], 0, i);return at1;}public static T valueOf(String s){return (T)Enum.valueOf(demo/T, s);}public static final T SPRING;public static final T SUMMER;private static final T ENUM$VALUES[];static{SPRING = new T("SPRING", 0);SUMMER = new T("SUMMER", 1);ENUM$VALUES = (new T[] {SPRING, SUMMER});}
    }
    
  • 通过反编译后代码我们可以看到,public final class T extends Enum,说明,该类是继承了Enum类的,同时final关键字告诉我们,这个类也是不能被继承的。

  • 当我们使用enmu来定义一个枚举类型的时候,编译器会自动帮我们创建一个final类型的类继承Enum类,所以枚举类型不能被继承。

内部类

  • 内部类又称为嵌套类,可以把内部类理解为外部类的一个普通成员。

  • 内部类之所以也是语法糖,是因为它仅仅是一个编译时的概念。

  • outer.java里面定义了一个内部类inner,一旦编译成功,就会生成两个完全不同的.class文件了,分别是outer.class和outer$inner.class。所以内部类的名字完全可以和它的外部类名字相同。

  • public class OutterClass {private String userName;public String getUserName() {return userName;}public void setUserName(String userName) {this.userName = userName;}public static void main(String[] args) {}class InnerClass{private String name;public String getName() {return name;}public void setName(String name) {this.name = name;}}
    }
    
  • 以上代码编译后会生成两个class文件:OutterClass$InnerClass.class 、OutterClass.class 。

  • 当我们尝试使用jad对OutterClass.class文件进行反编译的时候,命令行会打印以下内容:

  • Parsing OutterClass.class...
    Parsing inner class OutterClass$InnerClass.class...
    Generating OutterClass.jad
    
  • 他会把两个文件全部进行反编译,然后一起生成一个OutterClass.jad文件。文件内容如下:

  • public class OutterClass
    {class InnerClass{public String getName(){return name;}public void setName(String name){this.name = name;}private String name;final OutterClass this$0;InnerClass(){this.this$0 = OutterClass.this;super();}}public OutterClass(){}public String getUserName(){return userName;}public void setUserName(String userName){this.userName = userName;}public static void main(String args1[]){}private String userName;
    }
    

条件编译

  • —般情况下,程序中的每一行代码都要参加编译。但有时候出于对程序代码优化的考虑,希望只对其中一部分内容进行编译,此时就需要在程序中加上条件,让编译器只对满足条件的代码进行编译,将不满足条件的代码舍弃,这就是条件编译。

  • 如在C或CPP中,可以通过预处理语句来实现条件编译。其实在Java中也可实现条件编译。我们先来看一段代码:

  • public class ConditionalCompilation {public static void main(String[] args) {final boolean DEBUG = true;if(DEBUG) {System.out.println("Hello, DEBUG!");}final boolean ONLINE = false;if(ONLINE){System.out.println("Hello, ONLINE!");}}
    }
    
  • 反编译后代码如下:

  • public class ConditionalCompilation
    {public ConditionalCompilation(){}public static void main(String args[]){boolean DEBUG = true;System.out.println("Hello, DEBUG!");boolean ONLINE = false;}
    }
    
  • 首先,我们发现,在反编译后的代码中没有System.out.println(“Hello, ONLINE!”);,这其实就是条件编译。

  • 当if(ONLINE)为false的时候,编译器就没有对其内的代码进行编译。

  • 所以,Java语法的条件编译,是通过判断条件为常量的if语句实现的。根据if判断条件的真假,编译器直接把分支为false的代码块消除。通过该方式实现的条件编译,必须在方法体内实现,而无法在正整个Java类的结构或者类的属性上进行条件编译。

  • 这与C/C++的条件编译相比,确实更有局限性。在Java语言设计之初并没有引入条件编译的功能,虽有局限,但是总比没有更强。

断言

  • 在Java中,assert关键字是从JAVA SE 1.4 引入的,为了避免和老版本的Java代码中使用了assert关键字导致错误,Java在执行的时候默认是不启动断言检查的(这个时候,所有的断言语句都将忽略!)。

  • 如果要开启断言检查,则需要用开关-enableassertions或-ea来开启。

  • 看一段包含断言的代码:

  • public class AssertTest {public static void main(String args[]) {int a = 1;int b = 1;assert a == b;assert a != b : "";}
    }
    
  • 反编译后代码如下:

  • public class AssertTest {public AssertTest(){}public static void main(String args[])
    {int a = 1;int b = 1;if(!$assertionsDisabled && a != b)throw new AssertionError();{throw new AssertionError("H");} else{return;}
    }static final boolean $assertionsDisabled = !com/hs/suguar/AssertTest.desiredAssertionStatus();}
    
  • 很明显,反编译之后的代码要比我们自己的代码复杂的多。所以,使用了assert这个语法糖我们节省了很多代码。

  • 其实断言的底层实现就是if语言,如果断言结果为true,则什么都不做,程序继续执行,如果断言结果为false,则程序抛出AssertError来打断程序的执行。

  • -enableassertions会设置$assertionsDisabled字段的值。

数值字面量

  • 在java 7中,数值字面量,不管是整数还是浮点数,都允许在数字之间插入任意多个下划线。这些下划线不会对字面量的数值产生影响,目的就是方便阅读。

  • 比如:

  • public class Test {public static void main(String... args) {int i = 10_000;System.out.println(i);}
    }
    
  • 反编译后:

  • public class Test
    {public static void main(String[] args){int i = 10000;System.out.println(i);}
    }
    
  • 反编译后就是把_删除了。也就是说编译器并不认识在数字字面量中的_,需要在编译阶段把他去掉。

for-each

  • 增强for循环(for-each)相信大家都不陌生,日常开发经常会用到的,他会比for循环要少写很多代码.
  • 代码很简单,for-each的实现原理其实就是使用了普通的for循环和迭代器。

try-with-resource

  • Java里,对于文件操作IO流、数据库连接等开销非常昂贵的资源,用完之后必须及时通过close方法将其关闭,否则资源会一直处于打开状态,可能会导致内存泄露等问题。

  • 关闭资源的常用方式就是在finally块里是释放,即调用close方法。比如,我们经常会写这样的代码:

  • public static void main(String[] args) {BufferedReader br = null;try {String line;br = new BufferedReader(new FileReader("d:\\g.xml"));while ((line = br.readLine()) != null) {System.out.println(line);}} catch (IOException e) {// handle exception} finally {try {if (br != null) {br.close();}} catch (IOException ex) {// handle exception}}
    }
    
  • 从Java 7开始,jdk提供了一种更好的方式关闭资源,使用try-with-resources语句,改写一下上面的代码,效果如下:

  • public static void main(String... args) {try (BufferedReader br = new BufferedReader(new FileReader("d:\\g.xml"))) {String line;while ((line = br.readLine()) != null) {System.out.println(line);}} catch (IOException e) {// handle exception}
    }
    
  • 看,这简直是一大福音啊,虽然我之前一般使用IOUtils去关闭流,并不会使用在finally中写很多代码的方式,但是这种新的语法糖看上去好像优雅很多呢。

  • 反编译以上代码,看下他的背后原理:

  • public static transient void main(String args[]){BufferedReader br;Throwable throwable;br = new BufferedReader(new FileReader("d:\\ g.xml"));throwable = null;String line;try{while((line = br.readLine()) != null)System.out.println(line);}catch(Throwable throwable2){throwable = throwable2;throw throwable2;}if(br != null)if(throwable != null)try{br.close();}catch(Throwable throwable1){throwable.addSuppressed(throwable1);}elsebr.close();break MISSING_BLOCK_LABEL_113;Exception exception;exception;if(br != null)if(throwable != null)try{br.close();}catch(Throwable throwable3){throwable.addSuppressed(throwable3);}elsebr.close();throw exception;IOException ioexception;ioexception;}
    }
    
  • 其实背后的原理也很简单,那些我们没有做的关闭资源的操作,编译器都帮我们做了。

  • 所以,再次印证了,语法糖的作用就是方便程序员的使用,但最终还是要转成编译器认识的语言。

Lambda表达式

  • 关于lambda表达式,有人可能会有质疑,因为网上有人说他并不是语法糖。其实我想纠正下这个说法。
  • Labmda表达式不是匿名内部类的语法糖,但是他也是一个语法糖。实现方式其实是依赖了几个JVM底层提供的lambda相关api。
  • ambda表达式的实现其实是依赖了一些底层的api,在编译阶段,编译器会把lambda表达式进行解糖,转换成调用内部api的方式。

可能遇到的坑

泛型——当泛型遇到重载

  • public class GenericTypes {public static void method(List<String> list) {  System.out.println("invoke method(List<String> list)");  }  public static void method(List<Integer> list) {  System.out.println("invoke method(List<Integer> list)");  }  
    }  
    
  • 上面这段代码,有两个重载的函数,因为他们的参数类型不同,一个是List另一个是List,但是,这段代码是编译通不过的。因为我们前面讲过,参数List和List编译之后都被擦除了,变成了一样的原生类型List,擦除动作导致这两个方法的特征签名变得一模一样。

泛型——当泛型遇到catch

  • 泛型的类型参数不能用在Java异常处理的catch语句中。因为异常处理是由JVM在运行时刻来进行的。由于类型信息被擦除,JVM是无法区分两个异常类型MyException和MyException的

泛型——当泛型内包含静态变量

  • public class StaticTest{public static void main(String[] args){GT<Integer> gti = new GT<Integer>();gti.var=1;GT<String> gts = new GT<String>();gts.var=2;System.out.println(gti.var);}
    }
    class GT<T>{public static int var=0;public void nothing(T x){}
    }
    
  • 以上代码输出结果为:2!由于经过类型擦除,所有的泛型类实例都关联到同一份字节码上,泛型类的所有静态变量是共享的。

自动装箱与拆箱——对象相等比较

  • public static void main(String[] args) {Integer a = 1000;Integer b = 1000;Integer c = 100;Integer d = 100;System.out.println("a == b is " + (a == b));System.out.println(("c == d is " + (c == d)));
    }
    
  • 输出结果:

  • a == b is false
    c == d is true
    
  • 在Java 5中,在Integer的操作上引入了一个新功能来节省内存和提高性能。整型对象通过使用相同的对象引用实现了缓存和重用。

  • 适用于整数值区间-128 至 +127。

  • 只适用于自动装箱。使用构造函数创建对象不适用。

增强for循环

  • for (Student stu : students) {    if (stu.getId() == 2)     students.remove(stu);    
    }
    
  • 会抛出ConcurrentModificationException异常。

  • Iterator是工作在一个独立的线程中,并且拥有一个 mutex 锁。Iterator被创建之后会建立一个指向原来对象的单链索引表,当原来的对象数量发生变化时,这个索引表的内容不会同步改变,所以当索引指针往后移动的时候就找不到要迭代的对象,所以按照 fail-fast 原则 Iterator 会马上抛出java.util.ConcurrentModificationException异常。

  • 所以 Iterator 在工作的时候是不允许被迭代的对象被改变的。但你可以使用 Iterator 本身的方法remove()来删除对象,Iterator.remove() 方法会在删除当前迭代对象的同时维护索引的一致性。

  • 在这里插入图片描述

总结

  • 前面介绍了12种Java中常用的语法糖。由于篇幅问题,其他还有一些常见的语法糖比如字符串拼接其实基于 StringBuilder,Java10 里面的 var 关键字声明局部变量采用的是智能类型推断这里就不提了。
  • 所谓语法糖就是提供给开发人员便于开发的一种语法而已。但是这种语法只有开发人员认识。要想被执行,需要进行解糖,即转成JVM认识的语法。
  • 当我们把语法糖解糖之后,你就会发现其实我们日常使用的这些方便的语法,其实都是一些其他更简单的语法构成的。
  • 有了这些语法糖,我们在日常开发的时候可以大大提升效率,但是同时也要避免过渡使用。使用之前最好了解下原理,避免掉坑。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/85323.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

小米笔试题——01背包问题变种

这段代码的主要思路是使用动态规划来构建一个二维数组 dp&#xff0c;其中 dp[i][j] 表示前 i 个产品是否可以组合出金额 j。通过遍历产品列表和可能的目标金额&#xff0c;不断更新 dp 数组中的值&#xff0c;最终返回 dp[N][M] 来判断是否可以组合出目标金额 M。如果 dp[N][M…

thinkphp8路由

thinkphp8已出来有好一段时间了。这些天闲来无事&#xff0c;研究了下tp8的路由。默认情况下&#xff0c;tp8的路由是在route\app.php的文件里。但在实际工作中&#xff0c;我们并不会这样子去写路由。因为这样不好管理。更多的&#xff0c;是通过应用级别去管理路由。假如项目…

2023华为杯研究生数学建模C题分析

完整的分析查看文末名片获取&#xff01; 问题一 在每个评审阶段&#xff0c;作品通常都是随机分发的&#xff0c;每份作品需要多位评委独立评审。为了增加不同评审专家所给成绩之间的可比性&#xff0c;不同专家评审的作品集合之间应有一些交集。但有的交集大了&#xff0c;则…

conda的安装和使用

参考资料&#xff1a; https://www.bilibili.com/read/cv8956636/?spm_id_from333.999.0.0 https://www.bilibili.com/video/BV1Mv411x775/?spm_id_from333.999.0.0&vd_source98d31d5c9db8c0021988f2c2c25a9620 目录 conda是啥以及作用conda的安装conda的启动conda的配置…

redis如何清空当前缓存和所有缓存

Windows环境下使用命令行进行redis缓存清理 redis安装目录下输入cmdredis-cli -p 端口号flushdb 清除当前数据库缓存flushall 清除整个redis所有缓存keys * 查看所有key值del key 删除指定索引的值 注意&#xff1a; 我们清空缓存的时候&#xff0c;需要确保redis-…

JCEF中js与java交互、js与java相互调用

jcef中js与java相互调用&#xff0c;java与js相互调用&#xff0c;chrome与java相互调用&#xff0c;java与chrome相互调用、jcef与java相互调用 前提&#xff1a;https://blog.csdn.net/weixin_44480167/article/details/133170970&#xff08;java内嵌浏览器CEF-JAVA、jcef、…

Go 常用命令介绍

Go 常用命令 文章目录 Go 常用命令一、Go 常用命令1.1 go build1.1.1 指定输出目录1.1.2 常用环境变量设置编译操作系统和 CPU 架构1.1.3 查看支持的操作系统和CPU架构 1.2 go test1.3 go vet1.4 go clean1.5 go fmt1.6 go get1.7 go install1.8 go tool1.9 go generate1.10 go…

智思Ai企联系统去授权版本+uniapp前后端(内含教程)

智思AI企联系统是一款企业级AI系统&#xff0c;与普通版AI产品相比具备显著差异。该系统允许企业按需选择和定制二开任意功能&#xff0c;以满足不同企业的个性化需求和场景要求。企业可以根据实际业务需求扩展和改进系统功能模块&#xff0c;使之更好地适应企业独特需求。

【word格式】mathtype公式插入 | 段落嵌入后格式对齐 | 字体大小调整 |空心字体

1. 公式嵌入 推荐在线latex编辑器&#xff0c;可以截图转 latex 识别率很高 https://www.latexlive.com/home 美中不足&#xff0c;不开会员每天只能用3次识别。 通过公式识别后&#xff0c;输出选择align环境&#xff0c;然后在mathtype中直接粘贴latex就可以转好。 2.公式…

网络爬虫——HTTP和HTTPS的请求与响应原理

目录 一、HTTP的请求与响应 二、浏览器发送HTTP请求的过程 三、HTTP请求方法 四、查看网页请求 五、常用的请求报头 六、服务端HTTP响应 七、常用的响应报头 八、Cookie 和 Session 九、响应状态码 十、网页的两种加载方法 十一、认识网页源码的构成 十二、爬虫协议…

【Linux 之二】Ubuntu下开发环境的搭建(NFS \ SSH \ FTP \ Smba \ ...)

目前正在进行Linux相关项目的开发&#xff0c;而我的Linux开发是在Ubuntu&#xff08;版本20.04&#xff09;下进行的&#xff0c;为此需要搭建很多Linux相关的开发环境&#xff0c;方便工作的进行。这里主要是对各种开发环境的搭建做一个总结记录&#xff0c;方便后面查阅&…

LeetCode【69. x 的平方根】

给你一个非负整数 x &#xff0c;计算并返回 x 的 算术平方根 。 由于返回类型是整数&#xff0c;结果只保留 整数部分 &#xff0c;小数部分将被 舍去 。 注意&#xff1a;不允许使用任何内置指数函数和算符&#xff0c;例如 pow(x, 0.5) 或者 x ** 0.5 。 示例 1&#xff1…

FPGA原理与结构(0)——目录与传送门

一、 简介 FPGA的设计和软件设计不同&#xff0c;我们所设计的RTL代码最终还是要落实到硬件底层来进行实例化&#xff0c;因此理解硬件底层的内容是很有意义的。 二、可编程逻辑块CLB 可配置逻辑块CLB&#xff08;Configurable Logic Block&#xff09;是xilinx系类FPGA的基本…

操作系统--------调度算法篇

目录 一.先来先服务调度算法&#xff08;FCFS&#xff09; 二.短作业优先调度算法&#xff08;SJF&#xff09; 2.1.SJF调度算法缺点 三.优先级调度算法 3.1优先级调度算法的类型 1.非抢占优先级调度算法 2.抢占优先级调度算法 3.2优先级的类型 3.1静态优先级 3.2动态…

肖sir__mysql之存储练习题__013

实验 一、 实验要求&#xff1a; 理解存储过程的概念掌握存储过程的语法格式、使用方法掌握存 储过程的创建、执行 二、实验前提&#xff1a; – drop table if exists student; – Create table student – (Id varchar(255), #学号 – Name varchar(255), #姓名 – Roomid…

生成式模型和判别式模型区别

目录 1.概念 2.定义​ 3.举例​ &#xff08;1&#xff09;例子 A​ &#xff08;2&#xff09;例子 B​ 4.特点 5.优缺点 6.代表算法 1.概念 首先我们需要明确&#xff0c;两种不同的模型都用于监督学习任务中。监督学习的任务就是从数据中学习一个模型&#xff0c;并用…

golang优先级坑

看如下代码&#xff0c;我本以为a1, a2是相同的 package mainimport "fmt"func main() {b, c, d : 1, 0, 1a1 : b ^ c&(^d) // 1 ^a2 : c ^ b&(^d) // 0 ^fmt.Println(a1, a2) // 1 0 }但结果却是不同的&#xff0c;在golang中&的优先级^和&#xff5c;…

使用 PyTorch 的计算机视觉简介 (1/6)

一、说明 Computer Vision&#xff08;CV&#xff09;是一个研究计算机如何从数字图像和/或视频中获得一定程度的理解的领域。理解这个定义具有相当广泛的含义 - 它可以从能够区分图片上的猫和狗&#xff0c;到更复杂的任务&#xff0c;例如用自然语言描述图像。 二、CV常见的问…

RocketMQ高性能核心原理与源码架构剖析

文章目录 1、源码环境搭建1.1、主要功能模块1.2、源码启动服务1.2.1、 启动nameServer1.2.2、 启动Broker1.2.3、 发送消息1.2.4、 消费消息 2、源码剖析2.1、NameServer的启动过程2.2、Broker服务启动过程2.3、Netty服务注册框架2.3.1、关注重点2.3.2、源码重点 1、源码环境搭…

AJAX学习

文章目录 创建 XMLHttpRequest 对象向服务器发送请求XMLHttpRequest.open()XMLHttpRequest.send()GET或POST 服务器响应XMLHttpRequest 的属性XMLHttpRequest.readyStateXMLHttpRequest.onreadystatechangeXMLHttpRequest.responseXMLHttpRequest.responseTypeXMLHttpRequest.r…