-
1、Java基本数据类型及其对应的包装器类类型 Java中共用8种基本数据类型,并为这8种基本数据类型中的每一种都提供了一个包装器类,例如int类型对应的包装器类是Integer。具体类型如下表: 2、自动装箱和自动拆箱 自动装箱:就是指自动将基本数据类型转换为包装器类型。 自动拆箱:就是指自动将包装器类型转换为基本数据类型。 Integer i = 19; // 自动装箱 int j = i; // 自动拆箱 如上第一行代码,数组19是基本数据类型(int),当赋值给包装器类型(Integer)变量时,触发自动装箱操作,创建一个Integer类型对象,并且赋值给了 i 。其底层实际执行了以下代码: Integer i = Integer.valueOf(19); 同理,第二行代码,将 i 赋值给 j,触发了自动拆箱操作,将 i 中的数据取出赋值给 j 。其底层实际执行了以下代码: int j = i.intValue(); JDK5前是手动装箱和拆箱,JDK5之后就可以自动装箱和自动拆箱了。 Object o1 = true ? new Integer(1) : new Double(2.0); // 1.0 object o2; if(true) { o2 = new Integer(1); // 1 } else { o2 = new Double(2.0); } 如上第一个三元运算符输出的结果是1.0,因为三元运算符是一个整体,所以前面的Integer受后面的Double影响。而下面的if-else是无影响的,则输出1。 Integer i = 100; // 自动装箱 String str1 = i + ""; 以上只是以i的基本数值转成了字符串,并不会影响i的数据类型。 3、双等于“==" 通过” == “ 来比较对比的是栈中的值,基本数据类型比较的值,引用数据类型比较的是堆中内存对象的地址。即判断两个对象是否相等,实际上是在判断两个局部变量存储的地址是否相同,即是否指向相同的对象。 但如下代码中比较的a1和a2却是相等的。b1和b2又是不相等的。 Integer a1 = 58; Integer a2 = 58; Integer b1 = 129; Integer b2 = 129; Integer a3 = new Integer(58); System.out.println(a1 == a2); // true System.out.println(b1 == b2); // false System.out.println(a1 == a3); // false 实际是,Integer运用了 享元设计模式 来复用对象,当通过自动装箱,即调用 valueOf() 来创建Integer对象的时候,如果要创建的Integer对象的值在 -128~127之间,就会从IntegerCache类中直接返回,否则才会调用new方法创建新的对象。即a1和a2都是指向同一个对象(享元对象),b1和b2不是同一个对象。 而Integer a3 = new Integer(58);并不会调用valueOf(),即不会使用到IntegerCache。 为什么IntegerCache只缓存 -128~127之间的整型值? 当IntegerCache类被加载的时候,缓存的享元对象会被集中一次性创建好,而整型数值太多,不能一次性被创建,否则会占用太多的内存,类加载的时间会过长,即只能缓存大部分应用来说最常用的整型值,即一个字节的大小,byte数据类型的范围。 实际,jdk提供了自定义缓存的最大值,设置方法有 方法一: -Djava.lang.Integer.IntegerCache.high = 255 方法二: -XX:AutoBoxChaheMax = 255 4、字符串类型String String s1 = "小姐姐"; String s2 = "小姐姐"; String s3 = new String("小姐姐"); System.out.println(s1 == s2); // true System.out.println(s1 == s3); // false System.out.println(s1 == s3.intern()); // true System.out.println(s3 == s3.intern()); // false String类型利用了享元设计模式来复用相同的字符串常量,JVM会专门开辟一块存储区来存储字符串常量,这块存储区叫做”字符串常量池“ Integer类中要共享的对象是在类加载的时候就会集中一次性创建好,而字符串是在第一次被使用时被存储到常量池中,当之后再使用的时候,就直接引用常量池中已经存在的即可。 当调用intern()方法时,如果池已经包含一个等于此String对象的字符串,则返回池中的字符串。否则,将此 String 对象 添加到池中,并返回此String 对象的引用,即 intern()方法 最终返回的是常量池的地址(对象)。s3是重新new了一个对象,所以s3是指向堆的,而s3.intern()是指向常量池的,所以 s3不会等于s3.intern() 。 String s1 = "hello"; s1 = "haha"; String是一个final类,代表不可变的字符序列。字符串是不可变的,一个字符串对象一旦被分配,其内容是不可变的。即以上是先创建hello对象,之后在常量池中找是否有haha对象,没有就创建haha对象,再将s1指向haha对象,共创建了两个对象。 String a = "hello" + "abc"; 以上编译器会自动优化等价为:(即总共创建了一个对象) String a = "helloabc"; String a = "hello"; // 创建a对象 String b = "abc"; // 创建b对象 // 先创建一个StringBuilder sb = new StringBuilder() // 执行 sb.append("hello"); 再执行 sb.append("abc"); 最后转成string :String c = sb.toString() String c = a + b; // 最终c指向堆中的对象 Strin d = "helloabc"; System.out.print(c == d); // false d指向常量池,c指向堆 ———————————————— 原文链接:https://blog.csdn.net/weixin_43763430/article/details/127944433
-
一、自动装箱和自动拆箱的概念 自动装箱是指Java编译器(javac)在需要将基本数据类型转换为对应的包装类时,会自动进行转换的过程。换句话说,当我们使用基本数据类型时,编译器会自动将其转换为包装类。例如,当我们将一个int类型的变量赋值给一个Integer类型的变量时,就会发生自动装箱。 自动拆箱则是自动装箱的逆过程。当我们使用包装类对象进行计算或者与基本数据类型进行比较时,编译器会自动将包装类对象转换为对应的基本数据类型。例如,当我们将一个Integer类型的变量与一个int类型的变量相加时,就会发生自动拆箱。 二、自动装箱和自动拆箱的原理 在底层,自动装箱和自动拆箱是通过Java编译器的特殊处理来实现的。当编译器发现需要自动装箱时,它会在编译阶段将代码转换为创建包装类对象的代码。同样地,当编译器发现需要自动拆箱时,它会在编译阶段将代码转换为从包装类对象中提取基本数据类型的代码。 编译器执行自动装箱和自动拆箱流程: 识别自动装箱:当编译器遇到基本数据类型和对应的包装类之间的转换时,它会识别出需要进行自动装箱的地方。例如,当我们将一个int类型的变量赋值给一个Integer类型的变量时,编译器会发现这个转换操作需要自动装箱。 插入valueOf()方法调用:一旦编译器确定需要进行自动装箱,它会将代码转换为调用包装类的valueOf()方法来完成装箱操作。例如,将int赋值给Integer时,编译器会将代码转换为类似于Integer.valueOf(int)的形式。 生成装箱后的对象:valueOf()方法会返回一个对应的包装类对象,将基本数据类型的值封装起来。编译器会生成代码来创建这个包装类对象,并将其赋值给目标变量。例如,将int赋值给Integer时,编译器会生成代码类似于Integer obj = Integer.valueOf(intValue)。 识别自动拆箱:编译器还会识别需要进行自动拆箱的地方,例如,将一个包装类对象与基本数据类型进行比较或参与运算。 插入xxxValue()方法调用:一旦编译器确定需要进行自动拆箱,它会将代码转换为调用包装类的xxxValue()方法来提取基本数据类型的值。例如,将Integer与int进行相加时,编译器会将代码转换为类似于intValue()的形式。 使用提取的基本数据类型值:xxxValue()方法会从包装类对象中提取出基本数据类型的值,并将其用于运算或比较操作。编译器会生成代码来使用这个提取的基本数据类型值。例如,将Integer与int进行相加时,编译器会生成代码类似于int result = intValue + intValue2。 需要注意的是,自动装箱和自动拆箱虽然方便了开发和编码,但在一些特定的场景下也可能导致性能问题。因为自动装箱和自动拆箱涉及到对象的创建和销毁,可能会引发额外的内存开销和垃圾回收的压力。因此,在性能敏感的代码中,应该谨慎使用自动装箱和自动拆箱,可以选择使用基本数据类型来提高性能。 三、自动装箱和自动拆箱的应用 自动装箱和自动拆箱在Java编程中广泛应用于各种场景,特别是在集合框架中。例如,我们经常使用ArrayList来存储一组数据,而ArrayList只能存储对象类型。在这种情况下,我们可以使用自动装箱将基本数据类型转换为对应的包装类,然后将其添加到ArrayList中。 另一个常见的应用是在方法参数传递和返回值中。方法通常定义为使用包装类作为参数或返回值,以便能够处理各种类型的数据。通过自动装箱和自动拆箱,我们可以方便地在基本数据类型和包装类之间进行转换,从而使方法的调用更加灵活和方便。 此外,自动装箱和自动拆箱还可以简化条件判断和循环操作。例如,我们可以使用自动拆箱将一个Integer类型的变量与一个int类型的常量进行比较,而无需手动进行类型转换。 总结: 自动装箱和自动拆箱是Java语言中的一种语法糖,用于简化基本数据类型和包装类之间的转换过程。自动装箱通过调用valueOf()方法将基本数据类型转换为包装类对象,自动拆箱通过调用xxxValue()方法提取包装类对象中的基本数据类型值。这种特性在集合框架、方法参数传递和返回值等场景中得到广泛应用。 ———————————————— 原文链接:https://blog.csdn.net/DU9999999/article/details/132883399
-
为什么Java需要自动装箱和拆箱这一机制呢? 简单来说,就是有些历史原因的。 比如,泛型(Java5引入),Java中的泛型只能使用引用类型,不能使用基本数据类型。还有集合框架(Java2引入),Java 的集合框架(如 List、Set、Map 等)只能存储对象(引用类型),不能存储基本数据类型。因此为了API的兼容性,代码的灵活性和便利性,就有了自动装箱和拆箱机制。 下边一起来体会下这个机制。 1、Java 中的自动装箱 自动装箱是 Java 编译器在基本类型和它们对应的包装类之间进行的自动转换。例如,将 int 类型转换为 Integer 类型,将 double 类型转换为 Double 型等等。 如果转换以相反的方式进行,则称为拆箱。// 包装类和基本数据类型的相互转换 下面是一个最简单的自动装箱例子: Character ch = 'a'; 例如,下面的代码: List<Integer> li = new ArrayList<>(); for (int i = 1; i < 50; i += 2){ li.add(i); } 尽管将 int 值作为基本类型而不是 Integer 对象添加到 li 列表,但是代码仍然可以编译。这里 li 是一个 Integer 对象的列表,而不是 int 值的列表,为什么 Java 编译器不会发出编译时错误呢? 编译器之所以不会产生错误,是因为它从 i 创建了一个 Integer 对象,并将该对象添加到 li 列表。因此,编译器在运行时实际是将前面的代码转换为以下代码: List<Integer> li = new ArrayList<>(); for (int i = 1; i < 50; i += 2){ li.add(Integer.valueOf(i)); //装箱 } Integer.valueOf(i) 这段代码里有什么呢?//自动装箱 //源码版本:java17 public static Integer valueOf(int i) { if (i >= IntegerCache.low && i <= IntegerCache.high) return IntegerCache.cache[i + (-IntegerCache.low)]; return new Integer(i); } Java 在拆箱的过程中,创建了一个新的对象(new Integer),既然创建了额外的对象,就增加内存开销和垃圾回收的负担,这也是为什么要避免频繁进行大量的自动装箱和拆箱操作的原因。//自动装箱和拆箱涉及到创建和销毁额外的对象,即包装类对象 在以下情况下,Java 编译器会对基本类型的值进行自动装箱: 把基本类型的值作为参数传递给需要相应包装类的对象的方法。 把基本类型的值赋给相应包装类的变量。 2、Java 中的自动拆箱 接下来,看一下Java中的自动拆箱,例如下边的代码: public static int sumEven(List<Integer> li) { int sum = 0; for (Integer i: li){ if (i % 2 == 0){ sum += i; } return sum; } } 求余运算符 (%) 和一元运算符 (+=) 并不适用于 Integer 对象,但 Java 编译器在编译该方法时却不会产生任何错误,因为在调用 intValue() 方法时,Java 会将 Integer 转换为 int: public static int sumEven(List<Integer> li) { int sum = 0; for (Integer i : li) if (i.intValue() % 2 == 0) //拆箱操作 sum += i.intValue(); return sum; } 将包装类型(Integer)的对象转换为其对应的基本类型(int)称为拆箱。在以下情况下,Java 编译器会对包装类型的值进行自动拆箱: 把包装类型的值作为参数传递给需要相应基本类型的值的方法。 把包装类型的值赋值给相应基本类型的变量。 下边的例子展示了拆箱是如何工作的: import java.util.ArrayList; import java.util.List; public class Unboxing { public static void main(String[] args) { Integer i = new Integer(-8); // 1. 通过方法调用进行拆箱 int absVal = absoluteValue(i); System.out.println("absolute value of " + i + " = " + absVal); List<Double> ld = new ArrayList<>(); ld.add(3.1416); // Π 通过方法调用自动装箱。 // 2. 通过赋值进行拆箱 double pi = ld.get(0); System.out.println("pi = " + pi); } public static int absoluteValue(int i) { return (i < 0) ? -i : i; } } 该程序会打印以下内容: absolute value of -8 = 8 pi = 3.1416 3、自动装箱和拆箱总结 自动装箱和拆箱可以让开发人员编写更加清晰的代码,使代码更加易于阅读。下表列出了基本类型及其对应的包装类,Java 编译器会使用它们进行自动的装箱和拆箱: 基本类型 包装类型 boolean Boolean byte Byte char Character float Float int Integer long Long short Short double Double 自动装箱和拆箱的实现是通过 Java 编译器在编译时进行的。具体来说,Java 编译器会将自动装箱和拆箱操作转换为对应的方法调用,以实现基本数据类型和包装类型之间的转换。 但这在性能方面是要付出代价的。装箱后的值本质上就是把原始类型包裹起来,并保存在堆里。因此,装箱后的值需要更多的内存,并需要额外的内存搜索来获取被包裹的原始值。//所以,应该尽量避免不必要的装箱 4、在 Java 中怎样避免自动装箱和拆箱? 就一条原则:尽量使用基本数据类型 尽可能地使用基本数据类型,而不是对应的包装类型。基本数据类型的数据存储在栈中,而包装类型的对象存储在堆中,因此基本数据类型的操作比包装类型的操作更加高效。 至此,全文结束。 ———————————————— 原文链接:https://blog.csdn.net/swadian2008/article/details/128290566
-
什么是装箱和拆箱 装箱就是自动将基本数据类型转换为包装器类型。 拆箱就是自动将包装器类型转换为基本数据类型。 装箱和拆箱的原理 自动装箱都是通过包装类的 valueOf() 方法来实现的.自动拆箱都是通过包装类对象的 xxxValue() 来实现的。 场景介绍 包装类与基本数据类型进行比较运算,是先将包装类进行拆箱成基本数据类型,然后进行比较的。 两个包装类型之间的运算,会被自动拆箱成基本类型进行计算。 基本数据类型放入集合类中的时候,会进行自动装箱。 三目运算符的使用过程中。当第二,第三位操作数分别为基本类型和对象时,其中的对象就会拆箱为基本类型进行操作。 函数参数与返回值。 装箱缓存 装箱才会使用到缓存。 Byte, Short, Long有固定范围: -128 到 127。对于Character, 范围是 0 到 127。除了Integer以外,这个范围都不能改变。 总结 Ingeter是int的包装类,int的初始值为0,Ingeter的初始值为null。 无论如何,Integer与new Integer()不会相等。不会经历拆箱过程,Integer i8 = new Integer(127)的引用指向堆,而Integer i4 = 127指向专门存放他的内存(常量池),他们的内存地址不一样,使用 == 比较都为false。 两个都是非new出来的Integer,使用 == 比较,如果数在-128到127之间,则是true,否则为false。 他们的内存地址不一样,使用 == 比较都为false两个都是new出来的,==比较都为false。若要比较值是否相等,需使用equals方法进行比较。 int和Integer(无论new否)比较,都为true,因为会把Integer自动拆箱为int再去比。 实验 public class Test { public static void main(String[] args) { // 两个new出来的Integer类型的数据比较,相当于把new出来的地址作比较 Integer a0 = new Integer(1); Integer a1 = new Integer(1); System.out.println("Integer 对象作比较 a0 == a1: " + (a0 == a1)); // 调用intValue方法得到其int值 System.out.println("使用intValue得到int值作比较 a0 == a1: " + (a0.intValue() == a1.intValue())); // 把Integer类型的变量拆箱成int类型 int a2 = 1; System.out.println("将Integer自动拆箱 a1 == a2: " + (a1 == a2)); // Integer对象赋值比较,其实也是内存地址的比较 // 自动装箱,如果在-128到127之间,则值存在常量池中 Integer a3 = 30; Integer a4 = 30; System.out.println("Integer对象赋值比较 a3 == a4: " + (a3 == a4)); // Integer对象赋值(超过-128~127区间)比较 Integer a5 = 128; Integer a6 = 128; System.out.println("Integer对象赋值(超过-128~127区间)比较 a5 == a6: " + (a5 == a6)); // Integer对象赋值(超过-128~127区间)比较,调用intValue后再作比较 Integer a7 = 128; Integer a8 = 128; System.out.println("Integer对象赋值(超过-128~127区间)比较,调用intValue后 a7 == a8: " + (a7.intValue() == a8.intValue())); // 使用Integer类的equals()方法进行的是数值的比较 Integer a9 = 129; Integer a10 = 129; System.out.println("Integer类的equals()方法进行的是数值的比较 a9 == a10: " + a9.equals(a10)); } } ———————————————— 原文链接:https://blog.csdn.net/xiaowanzi_zj/article/details/122465712
-
自动装箱和拆箱问题是Java中一个老生常谈的问题了,今天我们就来一些看一下装箱和拆箱中的若干问题。本文先讲述装箱和拆箱最基本的东西,再来看一下面试笔试中经常遇到的与装箱、拆箱相关的问题。 一.什么是装箱?什么是拆箱? 我们知道 Java为每种基本数据类型都提供了对应的包装器类型,至于为什么会为每种基本数据类型提供包装器类型在此不进行阐述,有兴趣的朋友可以查阅相关资料。在Java SE5之前,如果要生成一个数值为10的Integer对象,必须这样进行: Integer i = new Integer(10); 而在从Java SE5开始就提供了自动装箱的特性,如果要生成一个数值为10的Integer对象,只需要这样就可以了: Integer i = 10; 这个过程中会自动根据数值创建对应的 Integer对象,这就是装箱。 那什么是拆箱呢?顾名思义,跟装箱对应,就是自动将包装器类型转换为基本数据类型: Integer i = 10; //装箱 nt n = i; //拆箱 简单一点说,装箱就是 自动将基本数据类型转换为包装器类型;拆箱就是 自动将包装器类型转换为基本数据类型。 下表是基本数据类型对应的包装器类型: int(4字节) Integer byte(1字节) Byte short(2字节) Short long(8字节) Long float(4字节) Float double(8字节) Double char(2字节) Character boolean(未定) Boolean 二.装箱和拆箱是如何实现的 上一小节了解装箱的基本概念之后,这一小节来了解一下装箱和拆箱是如何实现的。 我们就以Interger类为例,下面看一段代码: public class Main { public static void main(String[] args) { Integer i = 10; int n = i; } } 反编译class文件之后得到如下内容: 从反编译得到的字节码内容可以看出,在装箱的时候自动调用的是Integer的valueOf(int)方法。而在拆箱的时候自动调用的是Integer的intValue方法。 其他的也类似,比如Double、Character,不相信的朋友可以自己手动尝试一下。 因此可以用一句话总结装箱和拆箱的实现过程: 装箱过程是通过调用包装器的valueOf方法实现的,而拆箱过程是通过调用包装器的 xxxValue方法实现的。(xxx代表对应的基本数据类型)。 三.面试中相关的问题 虽然大多数人对装箱和拆箱的概念都清楚,但是在面试和笔试中遇到了与装箱和拆箱的问题却不一定会答得上来。下面列举一些常见的与装箱/拆箱有关的面试题。 1.下面这段代码的输出结果是什么? public class Main { public static void main(String[] args) { Integer i1 = 100; Integer i2 = 100; Integer i3 = 200; Integer i4 = 200; System.out.println(i1==i2); System.out.println(i3==i4); } } 也许有些朋友会说都会输出false,或者也有朋友会说都会输出true。但是事实上输出结果是: true false 为什么会出现这样的结果?输出结果表明i1和i2指向的是同一个对象,而i3和i4指向的是不同的对象。此时只需一看源码便知究竟,下面这段代码是Integer的valueOf方法的具体实现: public static Integer valueOf(int i) { if(i >= -128 && i <= IntegerCache.high) return IntegerCache.cache[i + 128]; else return new Integer(i); } 而其中IntegerCache类的实现为: private static class IntegerCache { static final int high; static final Integer cache[]; static { final int low = -128; // high value may be configured by property int h = 127; if (integerCacheHighPropValue != null) { // Use Long.decode here to avoid invoking methods that // require Integer's autoboxing cache to be initialized int i = Long.decode(integerCacheHighPropValue).intValue(); i = Math.max(i, 127); // Maximum array size is Integer.MAX_VALUE h = Math.min(i, Integer.MAX_VALUE - -low); } high = h; cache = new Integer[(high - low) + 1]; int j = low; for(int k = 0; k < cache.length; k++) cache[k] = new Integer(j++); } private IntegerCache() {} } 从这2段代码可以看出,在通过valueOf方法创建Integer对象的时候,如果数值在[-128,127]之间,便返回指向IntegerCache.cache中已经存在的对象的引用;否则创建一个新的Integer对象。 上面的代码中i1和i2的数值为100,因此会直接从cache中取已经存在的对象,所以i1和i2指向的是同一个对象,而i3和i4则是分别指向不同的对象。 2.下面这段代码的输出结果是什么? public class Main { public static void main(String[] args) { Double i1 = 100.0; Double i2 = 100.0; Double i3 = 200.0; Double i4 = 200.0; System.out.println(i1==i2); System.out.println(i3==i4); } } 也许有的朋友会认为跟上面一道题目的输出结果相同,但是事实上却不是。实际输出结果为: false false 至于具体为什么,读者可以去查看Double类的valueOf的实现。 在这里只解释一下为什么Double类的valueOf方法会采用与Integer类的valueOf方法不同的实现。很简单:在某个范围内的整型数值的个数是有限的,而浮点数却不是。 注意,Integer、Short、Byte、Character、Long这几个类的valueOf方法的实现是类似的。 Double、Float的valueOf方法的实现是类似的。 3.下面这段代码输出结果是什么: public static final Boolean TRUE = new Boolean(true); /** * The <code>Boolean</code> object corresponding to the primitive * value <code>false</code>. */ public static final Boolean FALSE = new Boolean(false); 至此,大家应该明白了为何上面输出的结果都是true了。 4.谈谈Integer i = new Integer(xxx)和Integer i =xxx;这两种方式的区别。 当然,这个题目属于比较宽泛类型的。但是要点一定要答上,我总结一下主要有以下这两点区别: 1)第一种方式不会触发自动装箱的过程;而第二种方式会触发; 2)在执行效率和资源占用上的区别。第二种方式的执行效率和资源占用在一般性情况下要优于第一种情况(注意这并不是绝对的)。 5.下面程序的输出结果是什么? public class Main { public static void main(String[] args) { Integer a = 1; Integer b = 2; Integer c = 3; Integer d = 3; Integer e = 321; Integer f = 321; Long g = 3L; Long h = 2L; System.out.println(c==d); System.out.println(e==f); System.out.println(c==(a+b)); System.out.println(c.equals(a+b)); System.out.println(g==(a+b)); System.out.println(g.equals(a+b)); System.out.println(g.equals(a+h)); } } 先别看输出结果,读者自己想一下这段代码的输出结果是什么。这里面需要注意的是:当 “==”运算符的两个操作数都是 包装器类型的引用,则是比较指向的是否是同一个对象,而如果其中有一个操作数是表达式(即包含算术运算)则比较的是数值(即会触发自动拆箱的过程)。另外,对于包装器类型,equals方法并不会进行类型转换。明白了这2点之后,上面的输出结果便一目了然: true false true true true false true 第一个和第二个输出结果没有什么疑问。第三句由于 a+b包含了算术运算,因此会触发自动拆箱过程(会调用intValue方法),因此它们比较的是数值是否相等。而对于c.equals(a+b)会先触发自动拆箱过程,再触发自动装箱过程,也就是说a+b,会先各自调用intValue方法,得到了加法运算后的数值之后,便调用Integer.valueOf方法,再进行equals比较。同理对于后面的也是这样,不过要注意倒数第二个和最后一个输出的结果(如果数值是int类型的,装箱过程调用的是Integer.valueOf;如果是long类型的,装箱调用的Long.valueOf方法)。 如果对上面的具体执行过程有疑问,可以尝试获取反编译的字节码内容进行查看。 ———————————————— 原文链接:https://blog.csdn.net/omygodvv/article/details/135480392
-
Java是一门面向对象的编程语言,不仅吸收了C++语言的各种优点,还摒弃了C++里难以理解的多继承、指针等概念,因此Java语言具有功能强大和简单易用两个特征。Java语言作为静态面向对象编程语言的代表,极好地实现了面向对象理论,允许程序员以优雅的思维方式进行复杂的编程。Java具有简单性、面向对象、分布式、健壮性、安全性、平台独立与可移植性、多线程、动态性等特点。 Java可以编写桌面应用程序、Web应用程序、分布式系统和嵌入式系统应用程序等。包装类: 在实际开发中,我们很多时候,需要将基本类型转为引用类型,便于后期的操作, 这个时候,java就给我们提供了8种包装类,分别是 基本类型 包装类 byte —Byte short — Short int — Integer long — Long char — Character float — Float double — Double boolean — Boolean 如何学习java中的类型 1:类型的解释 2:父类,子类,接口 3:jdk的版本 4:构造器 5:常用属性 6:常用方法 Integer 类在对象中包装了一个基本类型 int 的值。Integer 类型的对象包含一个 int 类型的字段。 父类:Number 父接口:Serializable,序列化接口 Comparable 自然比较接口 构造器 构造方法摘要 Integer(int value) 构造一个新分配的 Integer 对象,它表示指定的 int 值。 Integer(String s) 构造一个新分配的 Integer 对象,它表示 String 参数所指示的 int 值 属性 MAX_VALUE int的最大值 MIN_VALUE int的最小值 方法 转换方法 获取方法 设置方法 判断方法 注意:Integer范围值 在[-128,127] 的值,是直接声明在一个Integer数组中的,这个数组是一个final类型的数组 所以,存在的内存在常量池中,所以获取-128-127 之间的值,直接去常量池获取,即可,不用创建Integer对象,节省内存空间 其他几种包装类和Integer类似! 如何把字符串转为int? int a = Integer.parseInt(“100”)’ public static void main(String[] args) { //Integer it = new Integer(100); //自动装箱功能:把int类型自动装载到Integer中 先装箱,再赋值 //Integer it2 = 200; //Integer it3 = Integer.valueOf(100); //System.out.println(it2); //自动拆箱功能:把Integer类型中int值取出来 //Integer it4 = it2+it3; //先对it2 和 it3做拆箱功能,然后相加, 得到一个int类型的值,然后在对这个int类型的值 ,做装箱操作,最后再赋值 //Integer it4 = Integer.valueOf(it2.intValue() + it3.intValue()); //System.out.println(it4); //System.out.println(it2+100); 获取int类型的最大值和最小值 所以,存在的内存在常量池中,所以获取-128-127 之间的值,直接去常量池获取,即可,不用创建Integer对象,节省内存空间 int max_value = 2147483647; System.out.println(Integer.MAX_VALUE); System.out.println(Integer.MIN_VALUE); Integer it6 = new Integer("abc"); //NumberFormatException Integer it7 = 1000; byte b = it7.byteValue(); System.out.println(b); Integer it1 = 100; Integer it2 = 100; Integer it3 = new Integer(100); Integer it4 = new Integer(100); 在地址符做比较 System.out.println(it1==it2); //true System.out.println(it1==it3); //false System.out.println(it2==it3); //false System.out.println(it3==it4); //false System.out.println(it1.equals(it2)); System.out.println(it1.equals(it3)); System.out.println(it2.equals(it3)); System.out.println(it3.equals(it4)); 在数字上做比较 结果为0代表数字相等,结果为1,代表当前对象比你传进来的对象的值大,-1 反之则小 System.out.println(it1.compareTo(it2)); System.out.println(it1.compareTo(it4)); System.out.println(it4.compareTo(it1)); 将字符串解码为Integer System.out.println(Integer.decode("1001")+100); 在实际开发 System.out.println(0.00001+0.00004f); //数字必须精确 把Integer转为int Integer it = 1024; System.out.println(it.intValue()); 将字符串参数作为有符号的十进制整数进行解析。 /*System.out.println(Integer.parseInt("-100")); System.out.println(Integer.parseInt("100")); 使用第二个参数指定的基数,将字符串参数解析为有符号的整数。 System.out.println(Integer.parseInt("200", 10)); System.out.println(Integer.reverse(100));*/ 把10进制的数字转为2进制,8进制,16进制的字符串 System.out.println(Integer.toBinaryString(100)); System.out.println(Integer.toHexString(100)); System.out.println(Integer.toOctalString(100)); 把10进制的数字转为任意进制 范围2-36进制 System.out.println(Integer.toString(100, 16)); } ———————————————— 原文链接:https://blog.csdn.net/fengbingbing1/article/details/85267299
-
基本类型和包装类之间的转换基本类型和包装类之间经常需要互相转换,以 Integer 为例(其他几个包装类的操作雷同哦):在 JDK1.5 引入自动装箱和拆箱的机制后,包装类和基本类型之间的转换就更加轻松便利了。那什么是装箱和拆箱呢?我们分别来看下装箱:把基本类型转换成包装类,使其具有对象的性质,又可分为手动装箱和自动装箱拆箱:和装箱相反,把包装类对象转换成基本类型的值,又可分为手动拆箱和自动拆箱在实际转换时,使用Integer类的构造方法和Integer类内部的intValue方法实现这些类型之间的相互转换,实现的代码如下: int n = 10; Integer in = new Integer(100); //将int类型转换为Integer类型 Integer in1 = new Integer(n); //将Integer类型的对象转换为int类型 int m = in.intValue();JDK5.0的一个新特性是自动装箱和自动拆箱。 自动装箱 - 基本类型就自动地封装到与它相似类型的包装中,如:Integer i = 100; - 本质上是,编译器编译时自动添加:Integer i = new Integer(100); 自动拆箱 - 包装类对象自动转换成基本数据类型。如:int a = new Integer(100); - 本质是上,编译器编译时自动添加:int a = new Integer(100).intValue();扩展文章:JAVA菜鸟入门篇 - Java基本数据类型常用包装类(二十六)基本类型和字符串之间的转换在程序开发中,我们经常需要在基本数据类型和字符串之间进行转换。其中,基本类型转换为字符串有三种方法:1. 使用包装类的 toString() 方法2. 使用String类的 valueOf() 方法3. 用一个空字符串加上基本类型,得到的就是基本类型数据对应的字符串再来看,将字符串转换成基本类型有两种方法:1. 调用包装类的 parseXxx 静态方法2. 调用包装类的 valueOf() 方法转换为基本类型的包装类,会自动拆箱PS:其他基本类型与字符串的相互转化这里不再一一列出,方法都类似原文链接:https://blog.csdn.net/weixin_34197488/article/details/86393556
-
button11.setOnClickListener(v -> { try { ObsClient obsClient = new ObsClient(ak, sk,endPoint); // 流式下载 ObsObject obsObject = obsClient.getObject("namexiao", "Speedtestfile.mp4"); // 读取对象内容 InputStream input = obsObject.getObjectContent(); FileOutputStream fileOutputStream = new FileOutputStream("/Download/myfile.mp4"); byte[] b = new byte[1024]; //ByteArrayOutputStream bos = new ByteArrayOutputStream(); int len; while ((len = input.read(b)) != -1) { fileOutputStream.write(b, 0, len); }// System.out.println("getObjectContent successfully");// System.out.println(new String(bos.toByteArray())); fileOutputStream.close(); input.close(); } catch (ObsException e) { System.out.println("getObjectContent failed"); // 请求失败,打印http状态码 System.out.println("HTTP Code:" + e.getResponseCode()); // 请求失败,打印服务端错误码 System.out.println("Error Code:" + e.getErrorCode()); // 请求失败,打印详细错误信息 System.out.println("Error Message:" + e.getErrorMessage()); // 请求失败,打印请求id System.out.println("Request ID:" + e.getErrorRequestId()); System.out.println("Host ID:" + e.getErrorHostId()); e.printStackTrace(); } catch (Exception e) { System.out.println("getObjectContent failed"); // 其他异常信息打印 e.printStackTrace(); } });点击按键触发事件是程序闪退,求助大佬如何解决
-
支持Fast DFS、服务器、OSS等上传方式介绍在实际的业务中,可以根据客户的需求设置不同的文件上传需求,支持普通服务器上传+分布式上传(Fast DFS)+云服务上传OSS(OSS)软件架构为了方便演示使用,本项目使用的是前后端不分离的架构前端:Jquery.uploadFile后端:SpringBoot前期准备:FastDFS、OSS(华为)、服务器实现逻辑通过 application 配置对上传文件进行一个自定义配置,从而部署在不同客户环境可以自定义选择方式。优点:一键切换;支持当前主流方式;缺点:迁移数据难度增加:因为表示FileID在对象存储和服务器上传都是生成的UUID,而FastDFS是返回存取ID,当需要迁移的时候,通过脚本可以快速将FastDFS的数据迁移上云,因为存储ID可以共用。但是对象存储和服务器上传的UUID无法被FastDFS使用,增加迁移成本核心代码package com.example.file.util;import com.example.file.common.ResultBean;import com.github.tobato.fastdfs.domain.StorePath;import com.github.tobato.fastdfs.proto.storage.DownloadByteArray;import com.github.tobato.fastdfs.service.FastFileStorageClient;import com.obs.services.ObsClient;import com.obs.services.exception.ObsException;import com.obs.services.model.DeleteObjectRequest;import com.obs.services.model.GetObjectRequest;import com.obs.services.model.ObsObject;import com.obs.services.model.PutObjectResult;import io.micrometer.common.util.StringUtils;import jakarta.servlet.http.HttpServletResponse;import lombok.extern.slf4j.Slf4j;import org.springframework.mock.web.MockMultipartFile;import org.springframework.web.multipart.MultipartFile;import java.io.*;import java.util.Objects;import java.util.UUID;@Slf4jpublic class FileUtil { /** * * @param file 文件 * @param uploadFlag 标识 * @param uploadPath 上传路径 * @param endPoint 域名 * @param ak ak * @param sk sk * @param bucketName 桶名字 * @return fileId 用于下载 */ public static ResultBean uploadFile(MultipartFile file, String uploadFlag, String uploadPath, FastFileStorageClient fastFileStorageClient, String endPoint, String ak, String sk, String bucketName) { if (StringUtils.isBlank(uploadFlag)){ ResultBean.error("uploadFlag is null"); } switch (uploadFlag){ case "fastDFS": return uploadFileByFastDFS(file,fastFileStorageClient); case "huaweiOOS": return uploadFileByHuaweiObject(file, endPoint, ak, ak, bucketName); case "server": default: return uploadFileByOrigin(file,uploadPath); }} /** * 上传文件fastDFS * @param file 文件名 * @return */ private static ResultBean uploadFileByFastDFS(MultipartFile file,FastFileStorageClient fastFileStorageClient){ Long size=file.getSize(); String fileName=file.getOriginalFilename(); String extName=fileName.substring(fileName.lastIndexOf(".")+1); InputStream inputStream=null; try { inputStream=file.getInputStream(); //1-上传的文件流 2-文件的大小 3-文件的后缀 4-可以不管他 StorePath storePath=fastFileStorageClient.uploadFile(inputStream,size,extName,null); log.info("[uploadFileByFastDFS][FullPath]"+storePath.getFullPath()); return ResultBean.success(storePath.getPath()); }catch (Exception e){ log.info("[ERROR][uploadFileByFastDFS]"+e.getMessage()); return ResultBean.error(e.getMessage()); }finally { if (inputStream != null) { try { inputStream.close(); } catch (IOException e) { e.printStackTrace(); } } } } /** * 对象存储上传 * @param file 文件名 * @return */ private static ResultBean uploadFileByHuaweiObject(MultipartFile file, String huaweiEndPoint,String huaweiobsAk,String huaweiobsSk, String bucketName){ String fileName = file.getOriginalFilename(); fileName = renameToUUID(fileName); InputStream inputStream=null; try { inputStream=file.getInputStream(); // 创建ObsClient实例 ObsClient obsClient = new ObsClient(huaweiobsAk, huaweiobsSk, huaweiEndPoint); PutObjectResult result = obsClient.putObject(bucketName, fileName, inputStream); obsClient.close(); return ResultBean.success(fileName); }catch (ObsException e){ log.info("[ERROR][uploadFileByHuaweiObject]"+e.getErrorMessage()); return ResultBean.error(e.getErrorMessage()); }catch (Exception e){ log.info("[ERROR][uploadFileByHuaweiObject]"+e.getMessage()); return ResultBean.error(e.getMessage()); }finally { if (inputStream != null) { try { inputStream.close(); } catch (IOException e) { e.printStackTrace(); } } } } /** * 上传文件原本方法 * @param file 文件名 * @return */ private static ResultBean uploadFileByOrigin(MultipartFile file,String uploadPath){ String fileName = file.getOriginalFilename(); fileName = renameToUUID(fileName); File targetFile = new File(uploadPath); if (!targetFile.exists()) { targetFile.mkdirs(); } FileOutputStream out = null; try { out = new FileOutputStream(uploadPath + fileName); out.write(file.getBytes()); } catch (IOException e) { log.info("[ERROR][uploadFileByOrigin]"+e.getMessage()); return ResultBean.error(e.getMessage()); }finally { try { out.flush(); } catch (IOException e) { e.printStackTrace(); } try { out.close(); } catch (IOException e) { e.printStackTrace(); } } return ResultBean.success(fileName); } /** * 下载 * @return */ public static byte[] downloadFile(String fileId,String uploadFlag,String uploadPath ,FastFileStorageClient fastFileStorageClient, String group, String endPoint,String ak,String sk, String bucketName) { byte[] result=null; switch (uploadFlag){ case "fastDFS": result =downloadFileByFastDFS(fileId,fastFileStorageClient,group); break; case "huaweiOOS": result =downloadFileByHuaweiObject(fileId, endPoint, ak, sk, bucketName); break; case "server": default: String path2 = uploadPath + fileId; path2 = path2.replace("//", "/"); result=downloadFileByOrigin(path2); break; } return result; } /** * 下载文件fastDFS * @param fileId 文件名 * @return */ private static byte[] downloadFileByFastDFS(String fileId,FastFileStorageClient fastFileStorageClient, String group){ DownloadByteArray callback=new DownloadByteArray(); byte[] group1s=null; try { group1s = fastFileStorageClient.downloadFile(group, fileId, callback); }catch (Exception e){ log.info("[ERROR][downloadFileByFastDFS]"+e.getMessage()); } return group1s; } /** * 下载文件对象存储 * @param fileId 文件名 * @return */ private static byte[] downloadFileByHuaweiObject(String fileId, String huaweiEndPoint,String huaweiobsAk,String huaweiobsSk, String bucketName){ byte[] bytes =null; try { // 创建ObsClient实例 ObsClient obsClient = new ObsClient(huaweiobsAk, huaweiobsSk, huaweiEndPoint); // 构造GetObjectRequest请求 GetObjectRequest getObjectRequest = new GetObjectRequest(bucketName, fileId); // 执行下载操作 ObsObject obsObject = obsClient.getObject(getObjectRequest); bytes = inputStreamToByteArray(obsObject.getObjectContent()); // 关闭OBS客户端 obsClient.close(); return bytes; }catch (ObsException e){ log.info("[ERROR][downloadFileByHuaweiObject]"+e.getErrorMessage()); }catch (Exception e) { log.info("[ERROR][downloadFileByHuaweiObject]"+e.getMessage()); } return bytes; } /** * * @param input * @return * @throws IOException */ private static byte[] inputStreamToByteArray(InputStream input) throws IOException { ByteArrayOutputStream output = new ByteArrayOutputStream(); byte[] buffer = new byte[4096]; int n = 0; while (-1 != (n = input.read(buffer))) { output.write(buffer, 0, n); } return output.toByteArray(); } /** * 下载文件 * @param fileId 文件名 * @return */ private static byte[] downloadFileByOrigin(String fileId){ File file =new File(fileId); InputStream inputStream=null; byte[] buff = new byte[1024]; byte[] result=null; BufferedInputStream bis = null; ByteArrayOutputStream os = null; try { os=new ByteArrayOutputStream(); bis = new BufferedInputStream(new FileInputStream(file)); int i = bis.read(buff); while (i != -1) { os.write(buff, 0, buff.length); i = bis.read(buff); } result=os.toByteArray(); os.flush(); } catch (Exception e) { log.info("[ERROR][downloadFile]"+e.getMessage()); }finally { if (inputStream != null) { try { inputStream.close(); } catch (IOException e) { e.printStackTrace(); } } } return result; } /** * 删除文件 * @param fileId 文件ID * @return 删除失败返回-1,否则返回0 */ public static boolean deleteFile(String fileId,String fastDFSFlag,FastFileStorageClient fastFileStorageClient, String group,String uploadPath) { boolean result=false; if (StringUtils.isNotBlank(fastDFSFlag)&& fastDFSFlag.trim().equalsIgnoreCase("true")){ result =deleteFileByFastDFS(fileId,fastFileStorageClient,group); }else { String path2 = uploadPath + fileId; path2 = path2.replace("//", "/"); result=deleteByOrigin(path2); } return result; } private static boolean deleteByOrigin(String fileName) { File file = new File(fileName); // 如果文件路径所对应的文件存在,并且是一个文件,则直接删除 if (file.exists() && file.isFile()) { if (file.delete()) { return true; } else { return false; } } else { return false; } } private static boolean deleteFileByFastDFS(String fileId,FastFileStorageClient fastFileStorageClient, String group) { try { String groupFieId=group+"/"+fileId; StorePath storePath = StorePath.praseFromUrl(groupFieId); fastFileStorageClient.deleteFile(storePath.getGroup(), storePath.getPath()); } catch (Exception e) { log.info("[ERROR][deleteFileByFastDFS]"+e.getMessage()); return false; } return true; } /** * 生成fileId * @param fileName * @return */ private static String renameToUUID(String fileName) { return UUID.randomUUID() + "." + fileName.substring(fileName.lastIndexOf(".") + 1); } /** * 删除文件对象存储 * @param fileId 文件名 * @return */ private static boolean deleteFileByHuaweiObject(String fileId, String huaweiEndPoint,String huaweiobsAk,String huaweiobsSk, String bucketName){ try { // 创建ObsClient实例 ObsClient obsClient = new ObsClient(huaweiobsAk, huaweiobsSk, huaweiEndPoint); // 构造GetObjectRequest请求 DeleteObjectRequest getObjectRequest = new DeleteObjectRequest(bucketName, fileId); // 执行删除操作 obsClient.deleteObject(getObjectRequest); // 关闭OBS客户端 obsClient.close(); }catch (ObsException e){ log.info("[ERROR][deleteFileByHuaweiObject]"+e.getErrorMessage()); }catch (Exception e) { log.info("[ERROR][downloadFileByHuaweiObject]"+e.getMessage()); } return true; } /** * 文件数据输出(image) * @param fileId * @param bytes * @param res */ public static void fileDataOut(String fileId, byte[] bytes, HttpServletResponse res){ String[] prefixArray = fileId.split("\\."); String prefix=prefixArray[1]; File file =new File(fileId); res.reset(); res.setCharacterEncoding("utf-8");// res.setHeader("content-type", ""); res.addHeader("Content-Length", "" + bytes.length); if(prefix.equals("svg")) prefix ="svg+xml"; res.setContentType("image/"+prefix); res.setHeader("Accept-Ranges","bytes"); OutputStream os = null; try {// os = res.getOutputStream();// os.write(bytes);// os.flush(); res.getOutputStream().write(bytes); } catch (IOException e) { System.out.println("not find img.."); } finally { if (os != null) { try { os.close(); } catch (IOException e) { e.printStackTrace(); } } } }}参考资料假设个人实战使用可以采用docker快速安装,但是未安装过FastDFS建议普通安装部署,了解一下tracker和storage的使用,以及部署搭建集群。FastDFS普通安装部署:cid:link_2FasdDFS docker安装部署:https://blog.csdn.net/weixin_44621343/article/details/117825755?utm_medium=distribute.pc_relevant.none-task-blog-2defaultbaidujs_baidulandingword~default-0-117825755-blog-127896984.235v43pc_blog_bottom_relevance_base6&spm=1001.2101.3001.4242.1&utm_relevant_index=3OpenFeign和FastDFS的类冲突:cid:link_3Gitee地址:cid:link_1
-
Gson 是 Google 提供的用来在 Java 对象和 JSON 数据之间进行映射的 Java 类库。可以将一个 JSON 字符串转成一个 Java 对象,或者反过来。GsonUtilsimport com.google.gson.*;import com.google.gson.reflect.TypeToken;import lombok.SneakyThrows;import java.lang.reflect.Type;import java.time.LocalDate;import java.time.LocalDateTime;import java.time.LocalTime;import java.time.format.DateTimeFormatter;import java.util.List;import java.util.Map;import java.util.Objects;/** * @Author * @Date 2024/4 * @Des */public class GsonUtils { private static final DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"); private static final DateTimeFormatter dateFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd"); private static final DateTimeFormatter timeFormatter = DateTimeFormatter.ofPattern("HH:mm:ss"); private static final JsonSerializer<LocalDateTime> dateTimeSerializer = (obj, type, ctx) -> new JsonPrimitive(dateTimeFormatter.format(obj)); private static final JsonSerializer<LocalDate> dateSerializer = (obj, type, ctx) -> new JsonPrimitive(dateFormatter.format(obj)); private static final JsonSerializer<LocalTime> timeSerializer = (obj, type, ctx) -> new JsonPrimitive(timeFormatter.format(obj)); private static final JsonDeserializer<LocalDateTime> dateTimeDeserializer = (json, type, ctx) -> LocalDateTime.parse(json.getAsJsonPrimitive().getAsString(), dateTimeFormatter); private static final JsonDeserializer<LocalDate> dateDeserializer = (json, type, ctx) -> LocalDate.parse(json.getAsJsonPrimitive().getAsString(), dateFormatter); private static final JsonDeserializer<LocalTime> timeDeserializer = (json, type, ctx) -> LocalTime.parse(json.getAsJsonPrimitive().getAsString(), timeFormatter); private static final Gson gson; static { GsonBuilder builder = new GsonBuilder(); builder.disableHtmlEscaping(); builder.enableComplexMapKeySerialization(); // builder.excludeFieldsWithoutExposeAnnotation(); builder.setDateFormat("yyyy-MM-dd HH:mm:ss"); builder.registerTypeAdapter(LocalDateTime.class, dateTimeSerializer); builder.registerTypeAdapter(LocalDate.class, dateSerializer); builder.registerTypeAdapter(LocalTime.class, timeSerializer); builder.registerTypeAdapter(LocalDateTime.class, dateTimeDeserializer); builder.registerTypeAdapter(LocalDate.class, dateDeserializer); builder.registerTypeAdapter(LocalTime.class, timeDeserializer); gson = builder.create(); } public static Type makeJavaType(Type rawType, Type... typeArguments) { return TypeToken.getParameterized(rawType, typeArguments).getType(); } public static String toString(Object value) { if (Objects.isNull(value)) { return null; } if (value instanceof String) { return (String) value; } return toJSONString(value); } public static String toJSONString(Object value) { return gson.toJson(value); } public static String toPrettyString(Object value) { return gson.newBuilder().setPrettyPrinting().create().toJson(value); } public static JsonElement fromJavaObject(Object value) { JsonElement result = null; if (Objects.nonNull(value) && (value instanceof String)) { result = parseObject((String) value); } else { result = gson.toJsonTree(value); } return result; } @SneakyThrows public static JsonElement parseObject(String content) { return JsonParser.parseString(content); } public static JsonElement getJsonElement(JsonObject node, String name) { return node.get(name); } public static JsonElement getJsonElement(JsonArray node, int index) { return node.get(index); } @SneakyThrows public static <T> T toJavaObject(JsonElement node, Class<T> clazz) { return gson.fromJson(node, clazz); } @SneakyThrows public static <T> T toJavaObject(JsonElement node, Type type) { return gson.fromJson(node, type); } public static <T> T toJavaObject(JsonElement node, TypeToken<?> typeToken) { return toJavaObject(node, typeToken.getType()); } public static <E> List<E> toJavaList(JsonElement node, Class<E> clazz) { return toJavaObject(node, makeJavaType(List.class, clazz)); } public static List<Object> toJavaList(JsonElement node) { return toJavaObject(node, new TypeToken<List<Object>>() { }.getType()); } public static <V> Map<String, V> toJavaMap(JsonElement node, Class<V> clazz) { return toJavaObject(node, makeJavaType(Map.class, String.class, clazz)); } public static Map<String, Object> toJavaMap(JsonElement node) { return toJavaObject(node, new TypeToken<Map<String, Object>>() { }.getType()); } @SneakyThrows public static <T> T toJavaObject(String content, Class<T> clazz) { return gson.fromJson(content, clazz); } @SneakyThrows public static <T> T toJavaObject(String content, Type type) { return gson.fromJson(content, type); } public static <T> T toJavaObject(String content, TypeToken<?> typeToken) { return toJavaObject(content, typeToken.getType()); } public static <E> List<E> toJavaList(String content, Class<E> clazz) { return toJavaObject(content, makeJavaType(List.class, clazz)); } public static List<Object> toJavaList(String content) { return toJavaObject(content, new TypeToken<List<Object>>() { }.getType()); } public static <V> Map<String, V> toJavaMap(String content, Class<V> clazz) { return toJavaObject(content, makeJavaType(Map.class, String.class, clazz)); } public static Map<String, Object> toJavaMap(String content) { return toJavaObject(content, new TypeToken<Map<String, Object>>() { }.getType()); }}泛型实体类import com.google.gson.*;import com.google.gson.reflect.TypeToken;import lombok.SneakyThrows;import java.lang.reflect.Type;import java.time.LocalDate;import java.time.LocalDateTime;import java.time.LocalTime;import java.time.format.DateTimeFormatter;import java.util.List;import java.util.Map;import java.util.Objects;/** * @Author * @Date 2024/4 * @Des */public class GsonUtils { private static final DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss"); private static final DateTimeFormatter dateFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd"); private static final DateTimeFormatter timeFormatter = DateTimeFormatter.ofPattern("HH:mm:ss"); private static final JsonSerializer<LocalDateTime> dateTimeSerializer = (obj, type, ctx) -> new JsonPrimitive(dateTimeFormatter.format(obj)); private static final JsonSerializer<LocalDate> dateSerializer = (obj, type, ctx) -> new JsonPrimitive(dateFormatter.format(obj)); private static final JsonSerializer<LocalTime> timeSerializer = (obj, type, ctx) -> new JsonPrimitive(timeFormatter.format(obj)); private static final JsonDeserializer<LocalDateTime> dateTimeDeserializer = (json, type, ctx) -> LocalDateTime.parse(json.getAsJsonPrimitive().getAsString(), dateTimeFormatter); private static final JsonDeserializer<LocalDate> dateDeserializer = (json, type, ctx) -> LocalDate.parse(json.getAsJsonPrimitive().getAsString(), dateFormatter); private static final JsonDeserializer<LocalTime> timeDeserializer = (json, type, ctx) -> LocalTime.parse(json.getAsJsonPrimitive().getAsString(), timeFormatter); private static final Gson gson; static { GsonBuilder builder = new GsonBuilder(); builder.disableHtmlEscaping(); builder.enableComplexMapKeySerialization(); // builder.excludeFieldsWithoutExposeAnnotation(); builder.setDateFormat("yyyy-MM-dd HH:mm:ss"); builder.registerTypeAdapter(LocalDateTime.class, dateTimeSerializer); builder.registerTypeAdapter(LocalDate.class, dateSerializer); builder.registerTypeAdapter(LocalTime.class, timeSerializer); builder.registerTypeAdapter(LocalDateTime.class, dateTimeDeserializer); builder.registerTypeAdapter(LocalDate.class, dateDeserializer); builder.registerTypeAdapter(LocalTime.class, timeDeserializer); gson = builder.create(); } public static Type makeJavaType(Type rawType, Type... typeArguments) { return TypeToken.getParameterized(rawType, typeArguments).getType(); } public static String toString(Object value) { if (Objects.isNull(value)) { return null; } if (value instanceof String) { return (String) value; } return toJSONString(value); } public static String toJSONString(Object value) { return gson.toJson(value); } public static String toPrettyString(Object value) { return gson.newBuilder().setPrettyPrinting().create().toJson(value); } public static JsonElement fromJavaObject(Object value) { JsonElement result = null; if (Objects.nonNull(value) && (value instanceof String)) { result = parseObject((String) value); } else { result = gson.toJsonTree(value); } return result; } @SneakyThrows public static JsonElement parseObject(String content) { return JsonParser.parseString(content); } public static JsonElement getJsonElement(JsonObject node, String name) { return node.get(name); } public static JsonElement getJsonElement(JsonArray node, int index) { return node.get(index); } @SneakyThrows public static <T> T toJavaObject(JsonElement node, Class<T> clazz) { return gson.fromJson(node, clazz); } @SneakyThrows public static <T> T toJavaObject(JsonElement node, Type type) { return gson.fromJson(node, type); } public static <T> T toJavaObject(JsonElement node, TypeToken<?> typeToken) { return toJavaObject(node, typeToken.getType()); } public static <E> List<E> toJavaList(JsonElement node, Class<E> clazz) { return toJavaObject(node, makeJavaType(List.class, clazz)); } public static List<Object> toJavaList(JsonElement node) { return toJavaObject(node, new TypeToken<List<Object>>() { }.getType()); } public static <V> Map<String, V> toJavaMap(JsonElement node, Class<V> clazz) { return toJavaObject(node, makeJavaType(Map.class, String.class, clazz)); } public static Map<String, Object> toJavaMap(JsonElement node) { return toJavaObject(node, new TypeToken<Map<String, Object>>() { }.getType()); } @SneakyThrows public static <T> T toJavaObject(String content, Class<T> clazz) { return gson.fromJson(content, clazz); } @SneakyThrows public static <T> T toJavaObject(String content, Type type) { return gson.fromJson(content, type); } public static <T> T toJavaObject(String content, TypeToken<?> typeToken) { return toJavaObject(content, typeToken.getType()); } public static <E> List<E> toJavaList(String content, Class<E> clazz) { return toJavaObject(content, makeJavaType(List.class, clazz)); } public static List<Object> toJavaList(String content) { return toJavaObject(content, new TypeToken<List<Object>>() { }.getType()); } public static <V> Map<String, V> toJavaMap(String content, Class<V> clazz) { return toJavaObject(content, makeJavaType(Map.class, String.class, clazz)); } public static Map<String, Object> toJavaMap(String content) { return toJavaObject(content, new TypeToken<Map<String, Object>>() { }.getType()); }}示例//Result<String>:Result<String> source = GsonUtils.toJavaObject(c, new TypeToken<Result<String>>() {});String userStatus = Result.getSuccessResult(source, "");//Result<User>:Result<User> source = GsonUtils.toJavaObject(c, new TypeToken<Result<User>>() {});User userInfo = Result.getSuccessResult(source, null);//Result<List<Blog>>:Result<List<Blog>> source = GsonUtils.toJavaObject(c, new TypeToken<Result<List<Blog>>>() {});List<Blog> blogList = Result.getSuccessResult(source, Collections.emptyList());//Result<Map<String, Integer>>:Result<Map<String, Integer>> source = GsonUtils.toJavaObject(c, new TypeToken<Result<Map<String, Integer>>>() { });Map<String, Integer> statistics = Result.getSuccessResult(source, Collections.EMPTY_MAP);
-
前两天看前辈们的老代码,看到了一句神奇的SQL,生平第一次见: select ……………… from (select xx1,xx2,xx3,……,row_number() over(partition by xx4,xx5 order by xx6 desc,xx7 desc) rownum from xxxx_tbl where xxx8='sdfdsf' ……) where rownum =1 为防止公司说泄露源码,就只能这样表示一下意思了,这句sql的灵魂之处在于row_number() over(partition by xx4,xx5 order by xx6 desc,xx7 desc),你们见没见过我不知道,反正我以前没见过。所以一度没看懂。然后就一探究竟了,为了更加直观,我们还是拿前一篇limit重复问题里的那种表来进行举例。 案例 表结构如下: 字段 类型 注释 id varchar(20) 主键 col1 varchar(20) col1 col2 varchar(20) col2 col3 varchar(20) col3 全表查询: SELECT * FROM test1 ORDER BY col1 DESC; 数据为: id col1 col2 col3 15 5 9 10 12 2 5 6 14 2 7 8 16 2 4 5 11 1 2 3 其中col1字段不是唯一的,第二第三第四行的col1都是2。 上关键sql: SELECT id,col1,col2,col3, row_number() over (PARTITION BY col1 ORDER BY id DESC) AS row_num FROM test1 ; 返回结果: id col1 col2 col3 row_num 11 1 2 3 1 16 2 4 5 1 14 2 7 8 2 12 2 5 6 3 15 5 9 10 1 我们看这个返回结果,其中row_num列即为这段神奇的sql产生的,这一列的序号怎么来的呢:他是按照col1进行分组,然后按照id列进行倒序排序,row_num为分组后组内排序的结果。 分析 看完案例中的sql和执行结果,其实我们就能猜出开头的那个sql的目的了,他是要获取分组后每组的第一个值。其实我们经常会遇到这样的案例,比如:给你一个全年级学生的分数表,我要获取每个班分数最高的前三名。如果说这个年级有多少个班是已知的,我们可以通过union一个一个子查询拼接起来,但是如果班级个数未知,那这时候如果想用一句sql就有点无奈了。 同样,如果我们想要获取的是每个班级分数最高的一个人,我们也可以通过group by加max函数再加子查询解决,但是这里不是一个。 partition by与group by 一开始其实没搞明白,同样是分组partition by和group by有什么区别。从用法上来看: partition by select xx1,xx2,xx3,……,row_number() over(partition by xx4,xx5 order by xx6 desc,xx7 desc) rownum from xxxx_tbl group by select xx1,max(xx2) from xxxx_tbl group by xx1 partition by是用在返回参数中的,而group by是用在约束里的。而深层里去理解,partition by是分组后进行组内逐条分析,比如这里的row_number() over,而groupy by则是分组后进行整组的聚合分析,比如上面的max()。 扩展 既然是用于分析的,肯定有一些常用的与之配合的分析函数,比如group常和sum、min、max等组合使用。partition by除了上面的row_number外还有以下一些常用的配合: max:获取组内已排序的最大值 SELECT id,col1,col2,col3, MAX(col3) over (PARTITION BY col1 ORDER BY id DESC) AS row_num FROM test1 ; id col1 col2 col3 row_num 11 1 2 3 3 16 2 4 5 5 14 2 7 8 8 12 2 5 6 8 15 5 9 10 10 rank:排名的时候用row_number不是很好,原因是如果有两行order by的id相同,那么row_number就会漏掉其中的一行,而rank则不会漏,同时rank是跳跃排名,比如有两个第二名,那第四个就是第四名,而不是第三名 SELECT id,col1,col2,col3, rank() over (PARTITION BY col1 ORDER BY id DESC) AS row_num FROM test1 ; id col1 col2 col3 row_num 11 1 2 3 1 16 2 4 5 1 14 2 7 8 2 12 2 5 6 3 15 5 9 10 1 dense_rank:和rank一样,也是能够查出所有的记录,但是他不是跳跃排名,两个第二名之后是第三名。 SELECT id,col1,col2,col3, dense_rank() over (PARTITION BY col1 ORDER BY id DESC) AS row_num FROM test1 ; id col1 col2 col3 row_num 11 1 2 3 1 16 2 4 5 1 14 2 7 8 2 12 2 5 6 3 15 5 9 10 1 ———————————————— 原文链接:https://blog.csdn.net/qq_30095631/article/details/103558652
-
partition 子句This article will cover the SQL PARTITION BY clause and, in particular, the difference with GROUP BY in a select statement. We will also explore various use case of SQL PARTITION BY.本文将介绍SQL PARTITION BY子句,尤其是select语句中与GROUP BY的区别。 我们还将探讨SQL PARTITION BY的各种用例。We use SQL PARTITION BY to divide the result set into partitions and perform computation on each subset of partitioned data.我们使用SQL PARTITION BY将结果集划分为多个分区,并对分区数据的每个子集执行计算。准备样品数据 (Preparing Sample Data )Let us create an Orders table in my sample database SQLShackDemo and insert records to write further queries.让我们在示例数据库SQLShackDemo中创建一个Orders表,并插入记录以编写进一步的查询。Use SQLShackDemoGoCREATE TABLE [dbo].[Orders]( [orderid] INT, [Orderdate] DATE, [CustomerName] VARCHAR(100), [Customercity] VARCHAR(100), [Orderamount] MONEY)I use ApexSQL Generate to insert sample data into this article. Right click on the Orders table and Generate test data.我使用ApexSQL Generate将示例数据插入本文。 右键单击“订单”表并生成测试数据 。It launches the ApexSQL Generate. I generated a script to insert data into the Orders table. Execute this script to insert 100 records in the Orders table.它启动ApexSQL生成。 我生成了一个脚本,用于将数据插入到Orders表中。 执行此脚本以在Orders表中插入100条记录。USE [SQLShackDemo]GOINSERT [dbo].[Orders] VALUES (216090, CAST(N'1826-12-19' AS Date), N'Edward', N'Phoenix', 4713.8900)GOINSERT [dbo].[Orders] VALUES (508220, CAST(N'1826-12-09' AS Date), N'Aria', N'San Francisco', 9832.7200)GO…Once we execute insert statements, we can see the data in the Orders table in the following image.执行插入语句后,我们可以在下图中的Orders表中看到数据。We use SQL GROUP BY clause to group results by specified column and use aggregate functions such as Avg(), Min(), Max() to calculate required values.我们使用SQL GROUP BY子句按指定的列对结果进行分组,并使用诸如Avg(),Min(),Max()之类的聚合函数来计算所需的值。按功能分组 (Group By function syntax)SELECT expression, aggregate function ()FROM tablesWHERE conditionsGROUP BY expressionSuppose we want to find the following values in the Orders table假设我们要在“订单”表中找到以下值Minimum order value in a city一个城市的最小订单价值Maximum order value in a city城市中的最大订单价值Average order value in a city一个城市的平均订单价值Execute the following query with GROUP BY clause to calculate these values.使用GROUP BY子句执行以下查询以计算这些值。SELECT Customercity, AVG(Orderamount) AS AvgOrderAmount, MIN(OrderAmount) AS MinOrderAmount, SUM(Orderamount) TotalOrderAmountFROM [dbo].[Orders]GROUP BY Customercity;In the following screenshot, we can see Average, Minimum and maximum values grouped by CustomerCity.在以下屏幕截图中,我们可以看到按CustomerCity分组的平均值,最小值和最大值。Now, we want to add CustomerName and OrderAmount column as well in the output. Let’s add these columns in the select statement and execute the following code.现在,我们要在输出中也添加CustomerName和OrderAmount列。 让我们将这些列添加到select语句中,并执行以下代码。 SELECT Customercity, CustomerName ,OrderAmount, AVG(Orderamount) AS AvgOrderAmount, MIN(OrderAmount) AS MinOrderAmount, SUM(Orderamount) TotalOrderAmountFROM [dbo].[Orders]GROUP BY Customercity;Once we execute this query, we get an error message. In the SQL GROUP BY clause, we can use a column in the select statement if it is used in Group by clause as well. It does not allow any column in the select clause that is not part of GROUP BY clause.一旦执行此查询,我们将收到一条错误消息。 在SQL GROUP BY子句中,如果同时在Group by子句中使用它,则可以在select语句中使用一列。 它不允许select子句中的任何列不属于GROUP BY子句。We can use the SQL PARTITION BY clause to resolve this issue. Let us explore it further in the next section.我们可以使用SQL PARTITION BY子句解决此问题。 让我们在下一部分中进一步探讨它。SQL分区依据 (SQL PARTITION BY)We can use the SQL PARTITION BY clause with the OVER clause to specify the column on which we need to perform aggregation. In the previous example, we used Group By with CustomerCity column and calculated average, minimum and maximum values.我们可以将SQL PARTITION BY子句与OVER子句一起使用,以指定需要对其进行聚合的列。 在上一个示例中,我们将“分组依据”与“ CustomerCity”一起使用,并计算了平均值,最小值和最大值。Let us rerun this scenario with the SQL PARTITION BY clause using the following query.让我们使用以下查询,使用SQL PARTITION BY子句重新运行此方案。SELECT Customercity, AVG(Orderamount) OVER(PARTITION BY Customercity) AS AvgOrderAmount, MIN(OrderAmount) OVER(PARTITION BY Customercity) AS MinOrderAmount, SUM(Orderamount) OVER(PARTITION BY Customercity) TotalOrderAmountFROM [dbo].[Orders];In the output, we get aggregated values similar to a GROUP By clause. You might notice a difference in output of the SQL PARTITION BY and GROUP BY clause output.在输出中,我们获得类似于GROUP BY子句的聚合值。 您可能会注意到SQL PARTITION BY和GROUP BY子句输出的输出有所不同。Group BySQL PARTITION BYWe get a limited number of records using the Group By clauseWe get all records in a table using the PARTITION BY clause.It gives one row per group in result set. For example, we get a result for each group of CustomerCity in the GROUP BY clause.It gives aggregated columns with each record in the specified table.We have 15 records in the Orders table. In the query output of SQL PARTITION BY, we also get 15 rows along with Min, Max and average values.通过...分组SQL分区依据我们使用Group By子句获得的记录数量有限我们使用PARTITION BY子句获取表中的所有记录。它在结果集中为每组一行。 例如,我们在GROUP BY子句中为CustomerCity的每个组获取结果。它为指定表中的每条记录提供汇总列。我们在订单表中有15条记录。 在SQL PARTITION BY的查询输出中,我们还获得15行以及Min,Max和平均值。In the previous example, we get an error message if we try to add a column that is not a part of the GROUP BY clause.在上一个示例中,如果尝试添加不属于GROUP BY子句的列,则会收到一条错误消息。We can add required columns in a select statement with the SQL PARTITION BY clause. Let us add CustomerName and OrderAmount columns and execute the following query.我们可以使用SQL PARTITION BY子句在select语句中添加必需的列。 让我们添加CustomerName和OrderAmount列并执行以下查询。SELECT Customercity, CustomerName, OrderAmount, AVG(Orderamount) OVER(PARTITION BY Customercity) AS AvgOrderAmount, MIN(OrderAmount) OVER(PARTITION BY Customercity) AS MinOrderAmount, SUM(Orderamount) OVER(PARTITION BY Customercity) TotalOrderAmountFROM [dbo].[Orders];We get CustomerName and OrderAmount column along with the output of the aggregated function. We also get all rows available in the Orders table.我们获得CustomerName和OrderAmount列以及聚合函数的输出。 我们还将在Orders表中获得所有可用行。In the following screenshot, you can for CustomerCity Chicago, it performs aggregations (Avg, Min and Max) and gives values in respective columns.在以下屏幕截图中,您可以为CustomerCity Chicago进行聚合(平均,最小和最大)并在相应的列中提供值。Similarly, we can use other aggregate functions such as count to find out total no of orders in a particular city with the SQL PARTITION BY clause.同样,我们可以使用其他聚合函数(例如count)来通过SQL PARTITION BY子句找出特定城市的订单总数。SELECT Customercity, CustomerName, OrderAmount, COUNT(OrderID) OVER(PARTITION BY Customercity) AS CountOfOrders, AVG(Orderamount) OVER(PARTITION BY Customercity) AS AvgOrderAmount, MIN(OrderAmount) OVER(PARTITION BY Customercity) AS MinOrderAmount, SUM(Orderamount) OVER(PARTITION BY Customercity) TotalOrderAmountFROM [dbo].[Orders];We can see order counts for a particular city. For example, we have two orders from Austin city therefore; it shows value 2 in CountofOrders column.我们可以看到特定城市的订单计数。 例如,因此,我们有两个来自奥斯丁市的订单; 它在CountofOrders列中显示值2。带有ROW_NUMBER()的PARTITION BY子句 (PARTITION BY clause with ROW_NUMBER())We can use the SQL PARTITION BY clause with ROW_NUMBER() function to have a row number of each row. We define the following parameters to use ROW_NUMBER with the SQL PARTITION BY clause.我们可以将SQL PARTITION BY子句与ROW_NUMBER()函数一起使用,以获取每行的行号。 我们定义以下参数以将ROW_NUMBER与SQL PARTITION BY子句一起使用。PARTITION BY column – In this example, we want to partition data on PARTITION BY列 –在此示例中,我们要对CustomerCity column CustomerCity列上的数据进行分区Order By: In the ORDER BY column, we define a column or condition that defines row number. In this example, we want to sort data on the OrderAmount column排序依据:在ORDER BY列中,我们定义一列或条件来定义行号。 在此示例中,我们要对OrderAmount列上的数据进行排序SELECT Customercity, CustomerName, ROW_NUMBER() OVER(PARTITION BY Customercity ORDER BY OrderAmount DESC) AS "Row Number", OrderAmount, COUNT(OrderID) OVER(PARTITION BY Customercity) AS CountOfOrders, AVG(Orderamount) OVER(PARTITION BY Customercity) AS AvgOrderAmount, MIN(OrderAmount) OVER(PARTITION BY Customercity) AS MinOrderAmount, SUM(Orderamount) OVER(PARTITION BY Customercity) TotalOrderAmountFROM [dbo].[Orders];In the following screenshot, we get see for CustomerCity Chicago, we have Row number 1 for order with highest amount 7577.90. it provides row number with descending OrderAmount.在以下屏幕截图中,我们看到了CustomerCity Chicago ,我们的订单行1为最高金额7577.90。 它为行号提供降序的OrderAmount。具有累积总值的PARTITION BY子句 (PARTITION BY clause with Cumulative total value)Suppose we want to get a cumulative total for the orders in a partition. Cumulative total should be of the current row and the following row in the partition.假设我们要获得分区中订单的累计总数。 累积总数应该是分区中当前行和下一行的总和。For example, in the Chicago city, we have four orders.例如,在芝加哥市,我们有四个订单。CustomerCityCustomerNameRankOrderAmountCumulative Total RowsCumulative TotalChicagoMarvin17577.9Rank 1 +214777.51ChicagoLawrence27199.61Rank 2+314047.21ChicagoAlex36847.66Rank 3+48691.49ChicagoJerome41843.83Rank 41843.83客户城市顾客姓名秩订单金额累积总行累计总数芝加哥Maven1个7577.9等级1 +214777.51芝加哥劳伦斯27199.61等级2 + 314047.21芝加哥亚历克斯36847.66等级3 + 48691.49芝加哥杰罗姆41843.83等级41843.83In the following query, we the specified ROWS clause to select the current row (using CURRENT ROW) and next row (using 1 FOLLOWING). It further calculates sum on those rows using sum(Orderamount) with a partition on CustomerCity ( using OVER(PARTITION BY Customercity ORDER BY OrderAmount DESC).在下面的查询中,我们指定了ROWS子句以选择当前行(使用CURRENT ROW)和下一行(使用1 FOLLOWING)。 它还使用sum(Orderamount)和CustomerCity上的分区(使用OVER(PARTITION BY Customercity或ORDER BY OrderAmount DESC))来计算这些行上的总和。SELECT Customercity, CustomerName, OrderAmount, ROW_NUMBER() OVER(PARTITION BY Customercity ORDER BY OrderAmount DESC) AS "Row Number", CONVERT(VARCHAR(20), SUM(orderamount) OVER(PARTITION BY Customercity ORDER BY OrderAmount DESC ROWS BETWEEN CURRENT ROW AND 1 FOLLOWING), 1) AS CumulativeTotal,Similarly, we can calculate the cumulative average using the following query with the SQL PARTITION BY clause.同样,我们可以使用带有SQL PARTITION BY子句的以下查询来计算累积平均值。 SELECT Customercity, CustomerName, OrderAmount, ROW_NUMBER() OVER(PARTITION BY Customercity ORDER BY OrderAmount DESC) AS "Row Number", CONVERT(VARCHAR(20), AVG(orderamount) OVER(PARTITION BY Customercity ORDER BY OrderAmount DESC ROWS BETWEEN CURRENT ROW AND 1 FOLLOWING), 1) AS CumulativeAVG用PARTITION BY子句限制行的行数 (ROWS UNBOUNDED PRECEDING with the PARTITION BY clause)We can use ROWS UNBOUNDED PRECEDING with the SQL PARTITION BY clause to select a row in a partition before the current row and the highest value row after current row.我们可以将ROWS UNBOUNDED PRECEDING与SQL PARTITION BY子句一起使用,以选择分区中当前行之前的行以及当前行之后的最大值行。In the following table, we can see for row 1; it does not have any row with a high value in this partition. Therefore, Cumulative average value is the same as of row 1 OrderAmount.在下表中,我们可以看到第1行; 该分区中没有任何具有高值的行。 因此,累积平均值与第1行OrderAmount相同。For Row2, It looks for current row value (7199.61) and highest value row 1(7577.9). It calculates the average for these two amounts.对于第2行,它将查找当前行值(7199.61)和最高值行1(7577.9)。 它计算这两个数量的平均值。For Row 3, it looks for current value (6847.66) and higher amount value than this value that is 7199.61 and 7577.90. It calculates the average of these and returns.对于第3行,它将查找当前值(6847.66)和比该值更高的金额值7199.61和7577.90。 它计算这些平均值并返回。CustomerCityCustomerNameRankOrderAmountCumulative Average RowsCumulative AverageChicagoMarvin17577.9Rank 17577.90ChicagoLawrence27199.61Rank 1+27388.76ChicagoAlex36847.66Rank 1+2+37208.39ChicagoJerome41843.83Rank 1+2+3+45867.25客户城市顾客姓名秩订单金额累积平均行累积平均值芝加哥Maven1个7577.9等级17577.90芝加哥劳伦斯27199.61等级1 + 27388.76芝加哥亚历克斯36847.66等级1 + 2 + 37208.39芝加哥杰罗姆41843.83等级1 + 2 + 3 + 45867.25Execute the following query to get this result with our sample data.执行以下查询以通过我们的样本数据获得此结果。 SELECT Customercity, CustomerName, OrderAmount, ROW_NUMBER() OVER(PARTITION BY Customercity ORDER BY OrderAmount DESC) AS "Row Number", CONVERT(VARCHAR(20), AVG(orderamount) OVER(PARTITION BY Customercity ORDER BY OrderAmount DESC ROWS UNBOUNDED PRECEDING), 1) AS CumulativeAvgFROM [dbo].[Orders];结论 (Conclusion)In this article, we explored the SQL PARTIION BY clause and its comparison with GROUP BY clause. We also learned its usage with a few examples. I hope you find this article useful and feel free to ask any questions in the comments below在本文中,我们探讨了SQL PARTIION BY子句及其与GROUP BY子句的比较。 我们还通过一些示例了解了它的用法。 希望本文对您有所帮助,并随时在下面的评论中提问原文链接:https://blog.csdn.net/culuo4781/article/details/107618029
-
1.分表与表分区的区别 1.1 关于分表 分表是将一个大表分为几个或是多个小表,例如:table_1每天有1Kw的数据量,table_1随便时间的增长会越来越大,最终达到mysql表的极限,在这种比较极端的情况下 我们可以考虑对table_01进行分表操作,即每天生成与table_1表同样的表,每天一张即table_1_20120920 更多详细:http://blog.51yip.com/mysql/949.html 1.2 关于分区 以myisam为例子,mysql数据库中的数据是以文件的形势存在磁盘上,一张表主要对应着三个文件,一个是frm存放表结构文件,一个存放表数据的,一个是myi存表索引。 也就是将一个表文件分为多个表文件在磁盘上进行存取,提高对io的使用。 1.3 是否支持分区 mysql> show variables like ‘%partition%’; +——————-+——-+ | Variable_name | Value | +——————-+——-+ | have_partitioning | YES | +——————-+——-+ 出现YES表示当前版本支持表分区 1.4 查看分区表信息 select * from INFORMATION_SCHEMA.PARTITIONS where TABLE_SCHEMA=’tablename’ 2.如何分区 2.1 分区方法 分区有二个方法: 水平分区、垂直分区 2.2 分区的类型 === 水平分区的几种模式:=== * Range(范围) – 这种模式允许DBA将数据划分不同范围。例如DBA可以将一个表通过年份划分成三个分区,80年代(1980′s)的数据,90年代(1990′s)的数据以及任何在2000年(包括2000年)后的数据。 * Hash(哈希) – 这中模式允许DBA通过对表的一个或多个列的Hash Key进行计算,最后通过这个Hash码不同数值对应的数据区域进行分区,。例如DBA可以建立一个对表主键进行分区的表。 * Key(键值) – 上面Hash模式的一种延伸,这里的Hash Key是MySQL系统产生的。 * List(预定义列表) – 这种模式允许系统通过DBA定义的列表的值所对应的行数据进行分割。例如:DBA建立了一个横跨三个分区的表,分别根据2004年2005年和2006年值所对应的数据。 * Composite(复合模式) – 很神秘吧,哈哈,其实是以上模式的组合使用而已,就不解释了。举例:在初始化已经进行了Range范围分区的表上,我们可以对其中一个分区再进行hash哈希分区。 = 垂直分区(按列分)= 举个简单例子:一个包含了大text和BLOB列的表,这些text和BLOB列又不经常被访问,这时候就要把这些不经常使用的text和BLOB了划分到另一个分区,在保证它们数据相关性的同时还能提高访问速度。 2.2 代码演示 range分区如下: –按天进行划分 错误代码 create table part_range ( id bigint not null auto_increment, ftime date, str text )engine=myisam partition by range (ftime) ( partition p0 values less than (to_days(’2012-09-21′)), partition p1 values less than (to_days(’2012-09-22′)) ) 错误原因: 1. 在partition by range (ftime),ftime需要加to_days转成数字 2.进行分区的字段需要是主键的一部分1 使用以上语句创建时报错 ‘A PRIMARY KEY must include all columns in the table’s partitioning function” 默认分区限制分区字段必须是主键(PRIMARY KEY)的一部分 –按天进行分区 –直接使用时间列不可以,RANGE分区函数返回的列需要是整型。 create table part_range ( id bigint not null auto_increment, ftime date, str text, primary key(id,ftime) )engine=myisam partition by range (to_days(ftime)) ( partition p0 values less than (to_days(’2012-09-21′)), partition p1 values less than (to_days(’2012-09-22′)), PARTITION p3 VALUES LESS THAN MAXVALUE ); –按小时进行分区 create table part_range_day ( id bigint not null auto_increment, ftime datetime, str text, primary key(id,ftime) )engine=myisam partition by range(hour(ftime)) ( partition p0 values less than (1), partition p1 values less than(2), PARTITION p3 VALUES LESS THAN MAXVALUE ) [Err] 1493 – VALUES LESS THAN value must be strictly increasing for each partition 使用id进行划分时 –按id进行划分,id只能是由小到大 create table part_range_id ( id bigint not null auto_increment, ftime datetime, str text, primary key(id,ftime) )engine=myisam partition by range (id) ( partition p0 values less than (10000), partition p1 values less than (20000), partition p2 values less than maxvalue ) –使用list create table part_range_list ( id bigint not null auto_increment, ftime datetime, str text, primary key(id,ftime) )engine=myisam partition by list (id) ( partition p0 values in (0,1), partition p1 values in (2,4) ) –5.5之后的mysql说可以支持字符目前使用的5.5.24版本,使用字符时依然提示 [Err] 1697 – VALUES value for partition ‘p0′ must have type INT 3.性能测试 硬件:3.6G 内存 cpu Intel(R) Pentiun(R) 软件:win xp2 32位 –没有加分区表part_no_test,myisam引擎 create table part_no_test ( id bigint primary key auto_increment, ftime datetime, str text )engine=myisam –加入分区的表part_test,myisam引擎,以小时划分四个区 create table part_test ( id bigint auto_increment, ftime datetime, str text, primary key(id,ftime) )engine=myisam partition by range(hour(ftime)) ( partition p0 values less than (6), partition p1 values less than (12), partition p3 values less than (18), partition p4 values less than maxvalue ) –随机数据构造,构造2kw数据量 INSERT INTO part_test(ftime,str)values(FROM_UNIXTIME(unix_timestamp(’2012-09-20 08:00:00′)+FLOOR(7 + (RAND() * 360000))),’sss’); 生成数据存储过程 —生成part_no_test数据 drop procedure if exists part_no_insert_data; create procedure part_insert_data() begin set @id=20000000; while @id>0 do INSERT INTO part_no_test(ftime,str)values(FROM_UNIXTIME(unix_timestamp(’2012-09-20 08:00:00′)+FLOOR(7 + (RAND() * 360000))),RAND()*RAND()*100000000000); set @id=@id-1; end while; end; —生成part_test数据 drop procedure if exists part_insert_data; create procedure part_insert_data() begin set @id=20000000; while @id>0 do INSERT INTO part_test(ftime,str)values(FROM_UNIXTIME(unix_timestamp(’2012-09-20 08:00:00′)+FLOOR(7 + (RAND() * 360000))),RAND()*RAND()*100000000000); set @id=@id-1; end while; end; –有分区查询语句 select * from part_test a where a.ftime>’2012-09-20 10:00:00′ and a.ftime<’2012-09-20 12:00:00′; 执行时间:09.906s –无分区查询语句 select * from part_no_test a where a.ftime>’2012-09-20 10:00:00′ and a.ftime<’2012-09-20 12:00:00′; 执行时间:23.281s 附: Mysql可用的分区函数 DAY() DAYOFMONTH() DAYOFWEEK() DAYOFYEAR() DATEDIFF() EXTRACT() HOUR() MICROSECOND() MINUTE() MOD() MONTH() QUARTER() SECOND() TIME_TO_SEC() TO_DAYS() WEEKDAY() YEAR() YEARWEEK() 等 当然,还有FLOOR(),CEILING() 等,前提是使用这两个分区函数的分区健必须是整型。 要小心使用其中的一些函数,避免犯逻辑性的错误,引起全表扫描。 注: 1.分区的新增、删除每次只能是一个 2.maxvalues 后面不能再加分区 3.分区键必须包含在主键中 ERROR 1503 (HY000): A PRIMARY KEY must include all columns in the table’s partitioning function’ 4.ERROR 1503 (HY000): A UNIQUE INDEX must include all columns in the table’s partitioning function’说明在表上建约束索引(如唯一索引,普通索引可以)会有问题,必须把约束索引列包含在分区健内 5.只有RANGE和LIST分区才能有子分区,每个分区的子分区数量必须相同, 6. MYSQL将NULL值视为0.自动插入最小的分区中。 = 初步结论 = * 分区和未分区占用文件空间大致相同 (数据和索引文件) * 如果查询语句中有未建立索引字段,分区时间远远优于未分区时间 * 如果查询语句中字段建立了索引,分区和未分区的差别缩小,分区略优于未分区。 = 最终结论 = * 对于大数据量,建议使用分区功能。 * 去除不必要的字段 * 根据手册, 增加myisam_max_sort_file_size 会增加分区性能 ———————————————— 原文链接:https://blog.csdn.net/ls3648098/article/details/9353623
-
一、窗口函数的作用窗口函数是对一组值进行操作,不需要使用GROUP BY 子句对数据进行分组,还能够在同一行中同时返回基础行的列和聚合列。窗口函数,基础列和聚合列的查询都非常简单。二、语法格式 窗口函数的语法格式如下:1OVER([PARTITION BY value_expression,..[n] ] < ORDER BY BY_Clause>)PARTITION:分组;ORDER BY:排序;首先建一张调试表如下:123456CREATE TABLE [dbo].[xxx]([Id] [ int ] NULL ,[ Name ] [nvarchar](50) NULL ,[Operate] [nvarchar](50) NULL ,[Score] [ int ] NULL ,[CreateTime] [datetime] NULL ) ON [ PRIMARY ]往里面添加如下数据。三、应用场景1、聚合列与数据列共同显示12--查询姓名、分数、以及全体平均分SELECT Name , Score, CAST ( AVG (Score) OVER() AS decimal (5,2) ) AS '平均分' FROM xxx 2、分组日期最新1234--对每个人查询日期最新列SELECT * FROM ( SELECT row_number() OVER(PARTITION BY Name ORDER BY CreateTime) AS part ,Score, Name , CreateTime FROM xxx) AS CWHERE C.part = 13、分页返回结果集内的行号,每个分区从1开始,ORDER BY可确定在特定分区中为行分配唯一 ROW_NUMBER 的顺序。四、排名函数1、ROW_NUMBER()返回结果集内的行号,每个分区从1开始计算,ORDER BY可确定在特定分区中为行分配唯一 ROW_NUMBER 的顺序。1SELECT row_number() OVER(PARTITION BY Name ORDER BY CreateTime) ,Score, Name , CreateTime FROM xxx输出如下:2、RANK()返回结果集的分区内每行的排序。行的排名是从1开始算。如果两个或多个行与一个排名关联,则每个关联行将得到相同的排名。1SELECT RANK() OVER(PARTITION BY Name ORDER BY SCORE) ,Score, Name , CreateTime FROM xxx 下面一张图片很好地说明了Rank与ROW_NUMBER的区别。 3、DENSE_RANK() 返回结果集分区中行的排名,与Rank()类似,只是对并列的处理稍有不同,详见示例。SELECT DENSE_RANK() OVER(PARTITION BY Name ORDER BY SCORE) ,Score, Name, CreateTimeFROM xxx 下面的示例展示了Rank()与Dense_Rank()的区别。 4、NTILE() NTILE函数把结果中的行关联到组,并为每一行分配一个所属的组的编号,编号从1开始。对于每一个行,NTILE 将返回此行所属的组的编号。 如果分区的行数不能被 integer_expression(就是传入的那个参数,表示分几个组的意思) 整除,则将导致一个成员有两种大小不同的组。按照 OVER 子句指定的顺序,较大的组排在较小的组前面。--每个分区分2个组,该列是改行所属的组名SELECT NTILE(2) OVER(PARTITION BY Name ORDER BY SCORE) ,Score, Name, CreateTimeFROM xxx原文链接:https://blog.csdn.net/xoopx/article/details/51777733
-
1.分页查询 ROW_NUMBER() OVER(ORDER BY COLUMMS) IF OBJECT_ID('SysLogInfo','u') IS NULL --不存在用户表 BEGIN CREATE TABLE SysLogInfo( ID NVARCHAR(50) NOT NULL PRIMARY KEY, Name NVARCHAR(50) NULL, OldName NVARCHAR(50) NULL ) INSERT INTO dbo.SysLogInfo( ID, Name, OldName ) VALUES ( '1','张三','张二'), ( '2','李四','张二'), ( '3','赵武','赵六'), ( '4','钱7','赵六') END --分页查询 DECLARE @page INT =1 DECLARE @pageSize INT =2 SELECT ID,Name,OldName FROM ( SELECT *,ROW_NUMBER() OVER (ORDER BY ID asc) row FROM dbo.SysLogInfo) a WHERE row BETWEEN (@page-1)*@pageSize + 1 AND @page * @pageSize 2.ROW_NUMBER() OVER (PARTITION BY COL1 ORDER BY COL2) 可以返回一个分组的多条记录 SELECT *,ROW_NUMBER() OVER (PARTITION BY OldName ORDER BY ID ) COUNT FROM dbo.SysLogInfo 结果: partition by关键字是分析性函数的一部分,它和聚合函数(如group by)不同的地方在于它能返回一个分组中的多条记录,而聚合函数一般只有一条反映统计值的记录。 partition by用于给结果集分组,如果没有指定那么它把整个结果集作为一个分组。 partition by 与group by不同之处在于前者返回的是分组里的每一条数据,并且可以对分组数据进行排序操作。后者只能返回聚合之后的组的数据统计值的记录。 ———————————————— 原文链接:https://blog.csdn.net/wodemingzijiaoke/article/details/90612385
上滑加载中
推荐直播
-
HDC深度解读系列 - Serverless与MCP融合创新,构建AI应用全新智能中枢2025/08/20 周三 16:30-18:00
张昆鹏 HCDG北京核心组代表
HDC2025期间,华为云展示了Serverless与MCP融合创新的解决方案,本期访谈直播,由华为云开发者专家(HCDE)兼华为云开发者社区组织HCDG北京核心组代表张鹏先生主持,华为云PaaS服务产品部 Serverless总监Ewen为大家深度解读华为云Serverless与MCP如何融合构建AI应用全新智能中枢
回顾中 -
关于RISC-V生态发展的思考2025/09/02 周二 17:00-18:00
中国科学院计算技术研究所副所长包云岗教授
中科院包云岗老师将在本次直播中,探讨处理器生态的关键要素及其联系,分享过去几年推动RISC-V生态建设实践过程中的经验与教训。
回顾中 -
一键搞定华为云万级资源,3步轻松管理企业成本2025/09/09 周二 15:00-16:00
阿言 华为云交易产品经理
本直播重点介绍如何一键续费万级资源,3步轻松管理成本,帮助提升日常管理效率!
回顾中
热门标签