Excaliburer`s Zone

It was challenging, but not risky.

  博客园 :: 首页 :: 博问 :: 闪存 :: 新随笔 :: 联系 :: 订阅 订阅 :: 管理 ::

一. 代码基础类

1.DecimalFormat 和 BigDecimal

一般是涉及数据计算的时候用BigDecimal,涉及数据格式化表示的时候采用DecimalFormat。BigDecimal相比一般的float和double能够实现高精度的计算,而DecimalFormat能够实现丰富的数据表示。

DecimalFormat :

例1

import java.text.DecimalFormat;

public class Test{
    public static void main(String[] args){
        double pi=3.1415927;//圆周率
        //取一位整数 
        System.out.println(new DecimalFormat("0").format(pi));//3
        //取一位整数和两位小数  
        System.out.println(new DecimalFormat("0.00").format(pi));//3.14
        //取两位整数和三位小数,整数不足部分以0填补。  
        System.out.println(new DecimalFormat("00.000").format(pi));//03.142  
        //取所有整数部分  
        System.out.println(new DecimalFormat("#").format(pi));//3  
        //以百分比方式计数,并取两位小数  
        System.out.println(new DecimalFormat("#.##%").format(pi));//314.16%  
        
        long c=299792458;//光速  
        //显示为科学计数法,并取五位小数  
        System.out.println(new DecimalFormat("#.#####E0").format(c));//2.99792E8  
        //显示为两位整数的科学计数法,并取四位小数  
        System.out.println(new DecimalFormat("00.####E0").format(c));//29.9792E7  
        //每三位以逗号进行分隔。  
        System.out.println(new DecimalFormat(",###").format(c));//299,792,458  
        //将格式嵌入文本  
        System.out.println(new DecimalFormat("光速大小为每秒,###米").format(c)); //光速大小为每秒299,792,458米
    }
}

例2

DecimalFormat df1 = new DecimalFormat("0.0");   
DecimalFormat df2 = new DecimalFormat("#.#");   
DecimalFormat df3 = new DecimalFormat("000.000");   
DecimalFormat df4 = new DecimalFormat("###.###");   
System.out.println(df1.format(12.34));   //12.3
System.out.println(df2.format(12.34));   //12.3
System.out.println(df3.format(12.34));   //012.340
System.out.println(df4.format(12.34));   //12.34

DecimalFormat percentFormat = new DecimalFormat();   
percentFormat.applyPattern("#0.000%");   
System.out.println(percentFormat.format(0.3052222));//30.522%    

 BigDecimal(精确计算):

1.格式化方法:

  参考:笔记一:DecimalFormat & BigDecimal - 简书 (jianshu.com)

2.BigDecimal的比较

    public static void main(String[] args) {undefined
        BigDecimal a = new BigDecimal(1.2);
        BigDecimal b = new BigDecimal(2.1);

        // 比较之前确保a,b对象不能为空
        if (a.compareTo(b) == -1) {undefined
            System.out.println("a小于b");
        }
        if (a.compareTo(b) == 0) {undefined
            System.out.println("a等于b");
        }
        if (a.compareTo(b) == 1) {undefined
            System.out.println("a大于b");
        }

        if (a.compareTo(b) > -1) {undefined
            System.out.println("a大于等于b");
        }

        if (a.compareTo(b) < 1) {undefined
            System.out.println("a小于等于b");
        }

  (31条消息) java中BigDecimal的equals与compareTo的区别_ultramans1024的博客-CSDN博客_bigdecimal的compare

2.BeanUtils.copyProperties的使用

1)基础定义:

  • 浅拷贝可以理解为如果是引用类型,那么目标对象拷贝的只是源对象的地址,无论目标对象还是源对象改变,他们都会一起改变
  • 深拷贝就是将目标对象的属性全部复制一份给源对象,复制完之后他们就是隔开的,没有任何关系,无论操作源对象还是目标对象都对另一个没有影响
  • 无论是浅拷贝还是深拷贝,对于基本类型和String来说都是没有影响的,有影响的只有引用类型数据

BeanUtils.copyProperties是一个浅拷贝,用法为:

BeanUtils.copyProperties(source,target);//source是已有的数据,target是需要的数据

  

2)利用BeanUtils.copyPropertieslambda表达式实现深拷贝

需要注意的是,此处只能实现List的深拷贝,所以单个元素的话需要事先转化成一个List

例子:

    // 先构造
        fastdfsTestList = fastdfsTestList.stream().map(k -> {
            CityBean cityBean = new CityBean();
            cityBean.setValue("新的value");
            k.setCityBean(cityBean);
            return k;
        }).collect(Collectors.toList());

        // 实现深拷贝
        List<FastdfsTest> fastdfsTestList2 = fastdfsTestList.stream().map(k -> {
            FastdfsTest fastdfsTest = new FastdfsTest();
            BeanUtils.copyProperties(k,fastdfsTest);
            return fastdfsTest;
        }).collect(Collectors.toList());

关于Lambda表达式和相关关键字的用法可以参考:Java 8 Stream | 菜鸟教程 (runoob.com)  和 深入浅出 Java 8 Lambda 表达式 - OneAPM 博客 以及 Java Lambda 表达式 | 菜鸟教程 (runoob.com)

3.StringUtils中 isNotEmpty 和isNotBlank的区别

isNotEmpty(str)等价于 str != null && str.length > 0
isNotBlank(str) 等价于 str != null && str.length > 0 && str.trim().length > 0
同理
isEmpty 等价于 str == null || str.length == 0
isBlank  等价于 str == null || str.length == 0 || str.trim().length == 0

str.length > 0 && str.trim().length > 0  --->   str.length > 0

 总结一句话而言,isNotBlank相比 isNotEmpty更加严格了,把空格、换行符号(\n)、tab(\t) 都判断为空。 

4.QueryWrapper条件构造器使用大全

(1).基础用法

转自:(29条消息) mybatis plus 条件构造器queryWrapper学习_bird_tp的博客-CSDN博客_querywrapper

(2).进阶用法

(29条消息) MyBatis-Plus QueryWrapper及LambdaQueryWrapper的使用_简单随风的博客-CSDN博客_wrappers.lambdaquery

(3).MybatisPlus内置批量删除

方法1:使用内置的deleteBatchIds方法

 

 方法2:利用queryWrapper

 

 方法3:在Xml文件中自定义批量删除SQL语句

5.EasyPoi导出含有下拉框的Excel

参考:easypoi添加下拉预选值 - 我想回家 - 博客园 (cnblogs.com)

// poi导出,生成Workbook 
Workbook workbook = ExcelExportUtil.exportExcel(new ExportParams("生活老师导入","Sheet0", ExcelType.XSSF), ExportDormDTO.class, list);
// 设置下拉预选值
String[] dropDownValue = new String[]{"A", "B", "C", "D"};
String  dropDownSheetName= "生活评价";
XSSFDataValidationHelper dropDownValidationHelper = createXSSFDataValidationHelper(workbook, dropDownSheetName, dropDownValue);
DataValidationConstraint dropDOwnValidationConstraint = dropDownValidationHelper.createFormulaListConstraint(dropDownSheetName + "!$A$1:$A$" + dropDownValue.length);
Sheet firstSheet = workbook.getSheet("Sheet0");
CellRangeAddressList drowDownValueCoveringRowsAndCloumns = new CellRangeAddressList(2, firstSheet.getLastRowNum(), 5, 9);
XSSFDataValidation dropDownValidation =(XSSFDataValidation)dropDownValidationHelper.createValidation(dropDOwnValidationConstraint, drowDownValueCoveringRowsAndCloumns);
firstSheet.addValidationData(dropDownValidation);
downLoadExcel("生活老师评价导入.xlsx", response, workbook);

 ------------------------------------------------------------------------------- 下面是固定写法,是上面代码中使用到的局部方法------------------------------------------------------------------------------------------------

// 流导出
private static void downLoadExcel(String fileName, HttpServletResponse response, Workbook workbook) throws IOException {
    response.reset();
    response.setContentType("application/x-download");
    response.setHeader("Content-Disposition",
            "attachment; filename=" + new String(fileName.getBytes("utf-8"), "ISO-8859-1"));
    workbook.write(response.getOutputStream());
}

// 生成预选值的sheet页
private static XSSFDataValidationHelper createXSSFDataValidationHelper(Workbook workbook, String name, String[] strList) {
    Sheet sheet = workbook.createSheet(name);
    // 循环往该sheet中设置添加下拉列表的值
    for (int i = 0; i < strList.length; i++) {
        Row row = sheet.createRow(i);
        Cell cell = row.createCell((int) 0);
        cell.setCellValue(strList[i]);
    }
    workbook.setSheetHidden(workbook.getSheetIndex(name), true);//把生成的中间sheet页进行隐藏
    XSSFDataValidationHelper dvHelper = new XSSFDataValidationHelper((XSSFSheet) workbook.getSheet(name));
    dvHelper.createFormulaListConstraint(name + "!$A$1:$A$" + strList.length);
    return dvHelper;
}

  

6.Vue杂项 

(1).配置&&环境问题

一般是在package.json文件中修改相关依赖包的版本号,然后npm run pre进行编译,编译完成后才可以运行npm run dev 启动。

(2).Vue的生命周期

(3).Vue的常见用法

1.provideinject的用法:

一般而言,我们做一个父子组件数据传递,使用 props,当我们需要多层级向下子组件传递使用 providerinject 

比如:

父组件:

 

 子组件:

 

 和

 2.$ref$el的用法:

 参考:VUE的$refs和$el的使用 - 杨气 - 博客园 (cnblogs.com)

ref 被用来给元素或子组件注册引用信息

ref 有三种用法:
  1、ref 加在普通的元素上,用this.$refs.(ref值) 获取到的是dom元素

  2、ref 加在子组件上,用this.\$refs(ref值) 获取到的是组件实例,可以使用组件的所有方法。在使用方法的时候直接用this.\$refs(ref值).方法() 就可以使用了

    3、如何利用 v-for 和 ref 获取一组数组或者dom 节点

 

  如果通过v-for 遍历想加不同的ref时记得加 :号,即 :ref =某变量 ;

 

  这点和其他属性一样,如果是固定值就不需要加 :号,如果是变量记得加 :号。(加冒号的,说明后面的是一个变量或者表达式;没加冒号的后面就是对应的字符串常量(String)

 

应注意的坑有:

(1)、ref 需要在dom渲染完成后才会有,在使用的时候确保dom已经渲染完成。比如在生命周期 mounted(){} 钩子中调用,或者在 this.$nextTick(()=>{}) 中调用。

(2)、如果ref 是循环出来的,有多个重名,那么ref的值会是一个数组 ,此时要拿到单个的ref 只需要循环就可以了

 

vm.$el

获取Vue实例关联的DOM元素;

 

比方说我这里想获取自定义组件tabControl,并获取它的OffsetTop。就需要先获取该组件。

在组件内设置   属性 ref='一个名称(tabControl2)', 

然后 this.$refs.tabControl2     就拿到了该组件 

切记:ref属性,而获取组件的时候要用$refs

 

获取  OffsetTop,组件不是DOM元素,是没有OffsetTop的,无法通过 点 .OffsetTop来获取的。就需要通过$el来获取组件中的DOM元素

 

3.Vue中的then方法:

.then((data)=>{ })里的data是指接口成功返回的数据,包含请求头,请求体,等信息;

这里的then()方法有两个参数,第一个是成功时回调方法,默认给这个方法传递了成功的数据,另一个是失败的方法,以及失败的数据,第二个可选,

<script>
new Promise((resolve, reject) =>{
    setTimeout(() =>{
        //成功的时候调用resolve
        resolve('成功data')
        //失败的时候调用reject
        reject('error message')
    }, 1000)
}).then((data) =>{
    //处理成功后的逻辑
    console.log(data);//这个data 是接收的resolve参数--
}).catch((err) =>{
    console.log(err);
})
</script>

4.Vue中Watch的用法

watch是vue内部提供的一个用于侦听功能的更通用的方法,其用来响应数据的变化,通过特定的数据变化驱动一些操作。
vue官方文档解释当需要在数据变化时执行异步或开销较大的操作时,推荐使用该方法。

(1)基础用法之一:

<template>
<div></div>
</template>
<script>
export default {
    data(){
        variable:null,
    },
    watch:{
      // 此处监听variable变量,当期有变化时执行
        variable(item1,item2){
      // item1为新值,item2为旧值
    }
        }
            }
</script>

(2)基础用法之二:

<template>
<div></div>
</template>
<script>
	export default {
            data(){
            variable:null,
        },
        watch:{
            variable:{
              // 此处监听variable变量,当期有变化时执行
              handler(item1,item2){
              // item1为新值,item2为旧值
              }
                }
                    }
                     }
</script>

如上边两种用法所示:当变量variable产生变化时,会被页面侦听到并执行相关的操作。

(3)immediate的用法:

上面是watch侦听器的普通用法,就是当未设置immediate或者immediate的值为false时,被侦听的变量在页面初次加载时第一次绑定值的时候,并不会执行监听操作;但是当设置了immediate的值为true时,则会立刻执行一次监听操作。

<template>
<div></div>
</template>
<script>
	export default {
        data(){
        variable:null,
        },
    watch:{
        // 此处监听variable变量,当期有变化时执行
        variable(item1,item2){
        // item1为新值,item2为旧值
    },
        immediate:true // watch侦听操作内的函数会立刻被执行
        }
            }
</script>

(4)deep深度监听:

侦听普通变量的变化是使用以上方法,当侦听的某个变量值是对象时则不起作用,这时需要使用deep深度监听

<template>
<div></div>
</template>
<script>
	export default {
        data(){
        obj:{
            a:''
            },
        },
        watch:{
            // 此处监听obj属性a值变量
            'obj.a'(item1,item2){
            // item1为新值,item2为旧值
        },
        deep:true
            }
                }
</script>

如果对象内有多个属性,并采用以下写法,则对象内每个属性都会被侦听,每个属性的变化都会执行一次侦听操作。

<template>
<div></div>
</template>
<script>
export default {
    data(){
        obj:{
            a:'',
            b:'',
            c:''
        },
        },
        watch:{
            obj:{
            // 此处监听obj属性a值变量
            handler(item1,item2){
            // item1为新值,item2为旧值
        },
        deep:true
    } } } </script>

5.Object.assign()对象的拷贝

Object.assign() 方法用于将所有可枚举属性的值从一个或多个源对象复制到目标对象。它将返回目标对象。
Object.assign(target, ...sources)    【target:目标对象】,【souce:源对象(可多个)】
举个栗子:
const object1 = {
  a: 1,
  b: 2,
  c: 3
};

const object2 = Object.assign({c: 4, d: 5}, object1);

console.log(object2.c, object2.d);
console.log(object1)  // { a: 1, b: 2, c: 3 }
console.log(object2)  // { c: 3, d: 5, a: 1, b: 2 }

注意:
1.如果目标对象中的属性具有相同的键,则属性将被源对象中的属性覆盖。后面的源对象的属性将类似地覆盖前面的源对象的属性
2.Object.assign 方法只会拷贝源对象自身的并且可枚举的属性到目标对象。该方法使用源对象的[[Get]]和目标
对象的[[Set]],所以它会调用相关 getter 和 setter。因此,它分配属性,而不仅仅是复制或定义新的属性。如
果合并源包含getter,这可能使其不适合将新属性合并到原型中。为了将属性定义(包括其可枚举性)复制到
原型,应使用Object.getOwnPropertyDescriptor()和Object.defineProperty() 。

6.slot插槽的使用

(1)示例

 

 

 

 

 (2)用法详解:

参考:VUE3 之 插槽的使用 - 这个系列的教程通俗易懂,适合新手 - 简书 (jianshu.com) 和 彻底搞懂slot插槽,图文详解 - 简书 (jianshu.com)

有几个注意的点:

1.实际上插槽是定义在子组件中的,而插槽的使用是在父组件中的,所以插槽如何使用、是否使用由父组件决定。

2.插槽和props是类似的,但是通过props属性,父组件只能向子组件传递属性、方法;而通过插槽还可以传递带标签的内容,甚至是组件。

3.slot 标签不能直接绑定事件,通常的做法是在外层加一个 span 标签,然后把事件绑定到 span 标签上,类似于上面的示例。

7.杂项:

(1)Vue小数点保留成整数百分比

 注意:将小数转化为百分比时,必须使用.toFixed()保留需要的位数,否则会默认多出很多小数。

实例1:

let num = 1.1204950367;
    console.log(num.toFixed(4) * 100);  // 112.05000000000001
    let num2 = 1.1255432;
    console.log(num.toFixed(2));  // 1.12

实例2:

 

 

7.Java8中stream流的常见用法

 

 

stream()优点:

  无存储。stream不是一种数据结构,它只是某种数据源的一个视图,数据源可以是一个数组,Java容器或I/O channel等。
为函数式编程而生。对stream的任何修改都不会修改背后的数据源,比如对stream执行过滤操作并不会删除被过滤的元素,而是会产生一个不包含被过滤元素的新stream。
惰式执行。stream上的操作并不会立即执行,只有等到用户真正需要结果的时候才会执行。
可消费性。stream只能被“消费”一次,一旦遍历过就会失效,就像容器的迭代器那样,想要再次遍历必须重新生成。

 

 

 可参考:https://blog.csdn.net/Wang8309166/article/details/124619031

 

假设实体类为:

public class Person {

    private String name;

    private Integer age;


    public Person(String name, Integer age) {
        this.name = name;
        this.age = age;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public Integer getAge() {
        return age;
    }

    public void setAge(Integer age) {
        this.age = age;
    }
}

(1).生成实体对象

     //一个条件筛选
        Person result1 = list.stream().filter(p -> "moon".equals(p.getName()))
                .findAny().orElse(null);//如果找不到数据会返回null。orElse()是设置找不到数据后的默认值。
        System.out.println(result1.getName()); 


        //多个条件筛选
        Person result2 = list.stream().filter(p -> "oo".equals(p.getName()) && 18 == p.getAge())
                .findAny().orElse(new Person("liang", 20));
        System.out.println(result2.getName());  

(2).生成List

        List<String> names = list.stream().filter(p -> "moon".equals(p.getName())).map(Person::getName)
                .collect(Collectors.toList());
        names.forEach(System.out::println);   //moon

  其他:

List list= Arrays.asList(“a”, “b”, “c”, “d”);
List collect =list.stream().map(String::toUpperCase).collect(Collectors.toList());
System.out.println(collect); //[A, B, C, D]

数组所有元素,按某种规律计算:
List num = Arrays.asList(1,2,3,4,5);
List collect1 = num.stream().map(n -> n * 2).collect(Collectors.toList());
System.out.println(collect1); //[2, 4, 6, 8, 10]

(3).生成某个字段属性

 String name = list.stream().filter(p -> "moon".equals(p.getName())).map(Person::getName)
                .findAny().orElse("");  // moon
        System.out.println(name);

(4).生成数组

代码版本一:

public class Test11 {
    public static void main(String[] args) {
        //有如下7个元素黄药师,冯蘅,郭靖,黄蓉,郭芙,郭襄,郭破虏,使用Stream将以郭字开头的元素存入新数组
        String[] arr = {"黄药师","冯蘅","郭靖","黄蓉","郭芙","郭襄","郭破虏"};
        Object[] array = Arrays.stream(arr).filter(a -> a.startsWith("郭")).toArray();
        for (Object obj : array) {
            System.out.println(obj);
        }
    }
}

代码版本二:

public class Test11 {
    public static void main(String[] args) {
        //有如下7个元素黄药师,冯蘅,郭靖,黄蓉,郭芙,郭襄,郭破虏,使用Stream将以郭字开头的元素存入新数组
        Object[] array1 = Stream.of("黄药师", "冯蘅", "郭靖", "黄蓉", "郭芙", "郭襄", "郭破虏")
                .filter(a -> a.startsWith("郭")).toArray();
        for (Object obj : array1) {
            System.out.println(obj);
        }
    }
}

(5).生成Optional对象

  //在集合中查询出第一个用户密码为123456的用户
  Optional<User> user = list.stream().filter(userTemp -> "123456".equals(userTemp.getPassword())).findFirst();
​

(6).生成Map

参考:(31条消息) Stream流Collectors.toMap用法_星夜孤帆的博客-CSDN博客_collectors.tomap

public class Person {
    private Integer id;
    private String name;
 
    public static void main(String[] args) {
        List<Person> list = new ArrayList();
        list.add(new Person(1, "1"));
//        list.add(new Person(1, "4"));
        list.add(new Person(2, "2"));
        list.add(new Person(3, "3"));
 
        Map<Integer, Person> collect = list.stream().collect(Collectors.toMap(Person::getId, Function.identity()));
        Map<Integer, Person> collect1 = list.stream().collect(Collectors.toMap(Person::getId, Function.identity(), (a,b)->a));
        Map<Integer, Person> collect2 = list.stream().collect(Collectors.toMap(Person::getId, v -> v, (a,b)->a));
        Collection<Person> values = list.stream().collect(Collectors.toMap(Person::getId, Function.identity(), (a, b) -> a)).values();
        long count = list.stream().collect(Collectors.toMap(Person::getId, Function.identity(), (a, b) -> a)).values().stream().count();
        System.out.println(collect);
        System.out.println(collect1);
        System.out.println(collect2);
        System.out.println(values);
        System.out.println(count);
    }
}

  

使用toMap()函数之后,返回的就是一个Map了,自然会需要key和value。
toMap()的第一个参数就是用来生成key值的,第二个参数就是用来生成value值的。
第三个参数用在key值冲突的情况下:如果新元素产生的key在Map中已经出现过了,第三个参数就会定义解决的办法。

在.collect(Collectors.toMap(Person::getId, v -> v, (a,b)->a))中:

 

第一个参数:Person:getId表示选择Person的getId作为map的key值;

第二个参数:v->v表示选择将原来的对象作为Map的value值

第三个参数:(a,b)->a中,如果a与b的key值相同,选择a作为那个key所对应的value值。

如果key冲突,不加(a,b)->a会报如下错误

 

 

 

 

8.微服务Feign接口

(1) Feign接口调用异常信息

Feign调用的异常通常是定义的fallback类的内容,比如:

 

 

 而Feign中自定义抛出的异常其实是不会返回给客户端的,比如:

 

 

 异常信息内容·“查询的ID为空”实际并不能通过feign返回给客户端,只能在服务端的日志显示出来,实际返回给客户端的异常信息还是"HrIsCashSpecialFapplysFeginFallback getById error",也就是说Feign接口

异常信息都是调用fallback方法的结果,与自己定义的异常无关,如果要实现返回自定义的异常,可以参考:Feign调用时出现了异常但无法获取异常信息的问题怎么解决 - 开发技术 - 亿速云 (yisu.com)

9.多线程

(1) notify()、notifyAll()和wait()

首先有个结论:Synchronized关键字,可以理解为相当于获取了该对象监视器(对象锁)的使用权,而notify()、notifyAll()、wait()方法才是实际对对象监视器进行操。所以才有Synchronized关键字"阻塞"这一说法

10.注解

(1) Swagger注解

API开头相关的注解都是Swagger自带的,而不是Spring自带的注解
1.@ApiOperation
使用于在方法上,表示一个http请求的操作
源码中属性太多,记几个比较常用
value用于方法描述(一般只用这一个属性)
notes用于提示内容
tags可以重新分组(视情况而用)
2.@ApiImplicitParam
作用在方法上,表示单独的请求参数
参数:
1. name :参数名。
2. value : 参数的具体意义,作用。(类似注释)
3. required : 参数是否必填。
4. dataType :参数的数据类型。
5. paramType :查询参数类型,这里有几种形式:

类型 作用
path 以地址的形式提交数据
query 直接跟参数完成自动映射赋值
body 以流的形式提交 仅支持POST
header 参数在request headers 里边提交
3.@RequestParam
是获取前端传递给后端的参数,可以是get方式,也可以是post方式。其中如果前端传递的参数和后端你接受的参数起的名字字段是一致的可以省略不写,
也可以直接写@RequestParam String name,如果不一致一定要完整写,不然获取不到。

4.@PathVariable
是获取get方式,url后面参数,进行参数绑定

5.@RequestBody
@RequestBody主要用来接收前端传递给后端的json字符串中的数据的(请求体中的数据的);而最常用的使用请求体传参的无疑是POST请求了,
所以使用@RequestBody接收数据时,一般都用POST方式进行提交。在后端的同一个接收方法里,@RequestBody与@RequestParam()可以同时使用,
@RequestBody最多只能有一个,而@RequestParam()可以有多个。
6.@PostMapping
@PostMapping,处理post请求,等价于@RequestMapping(value = "/***",method = RequestMethod.POST)
7.@GetMapping
@GetMapping,处理GET请求,等价于@RequestMapping(value = "/***",method = RequestMethod.GET)
8.@ResponseBody
@responseBody注解的作用是将controller的方法返回的对象通过适当的转换器转换为指定的格式之后,写入到response对象的body区,通常用来返回JSON数据或者是XML
数据,需要注意的呢,在使用此注解之后不会再走试图处理器,而是直接将数据写入到输入流中,他的效果等同于通过response对象输出指定格式的数据。(不用再转换成JSON了)

11.Class对象

(1) 生成方式

 1、调用Object类的getClass()方法来得到Class对象,这也是最常见的产生Class对象的方法。比如:

MyObject x;
Class c1 = x.getClass();

Object.getClass();  Object中自带的方法,getclass(),返回一个class对象。

2、使用Class类的中静态forName()方法获得与字符串相应的Class对象。比如: 
    Class c2=Class.forName("MyObject"),MyObject必须是接口或者类的名字。

class.forname()  

Class c=Class.forName("类的全限定名")

传入string类型参数,要求jvm查找并加载指定的类,返回的是一个class对象的引用。

3、获取Class类型对象的第三个方法很easy。假设T是一个Java类型。那么T.class就代表了匹配的类对象。

比如

Class cl1 = Manager.class;
Class cl2 = int.class;
Class cl3 = Double[].class;

注意:Class对象实际上描写叙述的仅仅是类型。而这类型未必是类或者接口。

比如上面的int.class是一个Class类型的对象。

(2) Class.forName()用法

主要功能
Class.forName(xxx.xx.xx)返回的是一个类。
Class.forName(xxx.xx.xx)的作用是要求JVM查找并加载指定的类,也就是说JVM会执行该类的静态代码段。 

Class.forName是一个静态方法,相同能够用来载入类。

该方法有两种形式:Class.forName(String name, boolean initialize, ClassLoader loader)和 Class.forName(String className)。

第一种形式的參数 name表示的是类的全名;initialize表示是否初始化类。loader表示载入时使用的类载入器。

另外一种形式则相当于设置了參数 initialize的值为 true。loader的值为当前类的类载入器

(3) 什么时候用Class.forName()

 先来个热身,给你一个字符串变量,它代表一个类的包名和类名,你怎么实例化它?你第一想到的肯定是new,但是注意一点:

A a = (A)Class.forName(“pacage.A”).newInstance();

这和你 A a = new A(); 是一样的效果。

现在言归正传。
动态加载和创建Class 对象,比如想根据用户输入的字符串来创建对象时需要用到:

  • String str = “用户输入的字符串” ;
  • Class t = Class.forName(str);
  • t.newInstance();   

在初始化一个类,生成一个实例的时候,newInstance()方法和new关键字除了一个是方法,一个是关键字外,最主要有什么区别?

它们的区别在于创建对象的方式不一样,前者是使用类加载机制,后者是创建一个新类。

(3) newInstance和new关键字的区别

Java中工厂模式经常使用newInstance()方法来创建对象,因此从为什么要使用工厂模式上可以找到具体答案。 例如:

class c = Class.forName(“Example”);
factory = (ExampleInterface)c.newInstance();

其中ExampleInterface是Example的接口,可以写成如下形式:

String className = “Example”;
class c = Class.forName(className);
factory = (ExampleInterface)c.newInstance();

进一步可以写成如下形式:

String className = readfromXMlConfig;//从xml 配置文件中获得字符串
class c = Class.forName(className);
factory = (ExampleInterface)c.newInstance();

 

上面代码已经不存在Example的类名称,它的优点是,无论Example类怎么变化,上述代码不变,甚至可以更换Example的兄弟类Example2 , Example3 , Example4……,只要他们继承ExampleInterface就可以。

从JVM的角度看,我们使用关键字new创建一个类的时候,这个类可以没有被加载。但是使用newInstance()方法的时候,就必须保证
1、这个类已经加载;
2、这个类已经连接了
而完成上面两个步骤的正是Class的静态方法forName()所完成的,这个静态方法调用了启动类加载器,即加载 java API的那个加载器。

现在可以看出,newInstance()实际上是把new这个方式分解为两步,即首先调用Class加载方法加载某个类,然后实例化。

这样分步的好处是显而易见的。我们可以在调用class的静态加载方法forName时获得更好的灵活性,提供给了一种降耦的手段。

最后用最简单的描述来区分new关键字和newInstance()方法的区别:

newInstance: 弱类型。低效率。只能调用无参构造。
new: 强类型。相对高效。能调用任何public构造。
Class.forName(“”)返回的是类。
Class.forName(“”).newInstance()返回的是object

posted on 2022-02-10 15:41  Excaliburer  阅读(15)  评论(0编辑  收藏  举报