Java反射及新特性

Java

反射

1、单例模式的饿汉式和懒汉式中,即便私有化构造器,通过反射,也可以创建单例模式中类的多个对象。

2、通过反射,可以调用类中私有结构,而面向对象的封装性体现的是是否建议调用内部api,如果是private声明的结构,意味着不建议调用,因为内部封装了对应的更全面的结构,推荐调用此公开结构。

而反射指的是能否调用,表示只要类的结构都调用到内存中,我们都有能力进行调用。

3、Class类的理解

运行时类:加载到内存中的类

4、获取Class实例的三种方式

public void test() throws ClassNotFoundException {
    //运行类在内存中会缓存起来,在整个执行期间,只会加载一次 所以下面的==都为true

    //方式1. 调用运行时类的静态属性:class
    Class  clazz1=User.class;
    System.out.println(clazz1);

    //方式2. 调用运行时类的对象的getClass()
    User u1=new User();
    Class clazz2 = u1.getClass();
    System.out.println(clazz2==clazz1);

    //方式3. 调用Class的静态方法forName(String className)
    String name="com.atguigu02._class.User";//全类名
    Class clazz3 = Class.forName(name);
    System.out.println(clazz1==clazz3);
}

实例指向的可以是java的所有数据类型,包括void,Class本身等

只要元素类型和维度相同,则获得的Class实例相等。

补充知识:(对象是抽象的说法,你可以把它看作一个符合它自身定义的所有实例的代表,而实例则是对象的现实体现。你可以说一个实例是一个对象,但你不能说一个对象是一个实例。因为定义对象时只是把自身的规则和逻辑定义好,实例可以通过输入不同的数据使得自己和其他实例不同。  
  比如你可以定义一个Wheel.java   它是对象:而Test.java   中可以定义两个wheel的实例。)

5、类的加载器

启动类装载器/引导类装载器、扩展类装载器和应用程序装载器是层次关系,没有父子关系。

6、使用类的加载器获取流

public void test2() throws IOException {
    Properties pro=new Properties();

    //通过类加载器读取文件的默认路径为:当前module下的src下
    //用File读取文件的相对默认路径为:当前module下
    InputStream is = ClassLoader.getSystemClassLoader().getResourceAsStream("info.properties");

    pro.load(is);

    String name = pro.getProperty("name");
    String password = pro.getProperty("password");
    System.out.println(name+":"+password);

}

7、当使用构造器创建实例,结果没有相应的类初始化构造器,则会报InstantiationException;

   当使用的构造器为私有构造器时,会报ILLegalAccessException异常。

 

8、为什么一定要有一个显示的无参构造器?

 

9、int.class 和Integer.class是两个完全不同的类型,只有值才能自动装箱。

10、注解可以参照SuppressWarnings来写

11、调用指定的结构

 

12、反射的好处主要体现在动态性,让数据和代码分开

13、使用Class.forName()执行了类构造器中的clinit方法

新特性

Lambda表达式

1、常见的函数式接口:Comparator\Runable\java.util.function下定义的丰富接口

2、方法引用、构造器引用,数组引用使用的条件:就是在写抽象方法时调用的是其它抽象方法,且满足一些特征(其它抽象方法参数和原抽象方法函数之间的个数相等或少一个,返回的数据类型相同等),才可以进行替换。

 

3、接口中的方法全是抽象方法,要想使用Lambda表达式,该接口必须有且只有一个抽象方法

方法引用
 public class MethodRefTest {
    //情况一:对象::实例方法
    //Consumer中的void accept(T t)
    @Test
    public void test1(){
        Consumer<String> con1=new Consumer<String>() {
            @Override
            public void accept(String s) {
                System.out.println(s);
            }
        };

        //Lambda
        Consumer<String> con2=s-> System.out.println(s);

        //方法引用
        Consumer<String> con3=System.out::println;
    }

    //Supplier中的T get()
    @Test
    public void test2(){
        Employee emp=new Employee("ma",22);
        Supplier<String> sup1=new Supplier<String>() {
            @Override
            public String get() {
                return emp.getName();
            }
        };

        //Lambda
        Supplier<String> sup2=()->emp.getName();

        //方法引用
        Supplier<String> sup3=emp::getName;
    }

    //Function中的R apply(T t)
    @Test
    public void test3(){
        Function<Double,Long> fun1=new Function<Double, Long>() {
            @Override
            public Long apply(Double aDouble) {
                return Math.round(aDouble);
            }
        };

        //Lambda
        Function<Double,Long> fun2=adouble->Math.round(adouble);

        //方法引用
        Function<Double,Long> fun3=Math::round;
    }

    @Test
    public void test4(){
        BiPredicate<String,String> bipre1=new BiPredicate<String, String>() {
            @Override
            public boolean test(String s, String s2) {
                return s.equals(s2);
            }
        };

        //Lambda
        BiPredicate<String,String> bipre2=(s1,s2)->s1.equals(s2);

        //方法引用
        BiPredicate<String,String> bipre3=String::equals;
    }
}

 

构造器引用
 @Test
public void test3(){
  //判断函数式接口 有两个形参并返回一个类型的值
    BiFunction<String,Integer,Employee> bif1=new BiFunction<String, Integer, Employee>() {
        @Override
        public Employee apply(String s, Integer integer) {
            return new Employee(s,integer);
        }
    };
    System.out.println(bif1.apply("Tom",12));

    //构造器引用
    BiFunction<String,Integer,Employee> bif2=Employee::new;
    System.out.println(bif2.apply("Jack",20));
}

4、函数式接口:接口中只声明一个抽象方法

StreamAPI

1、stream不会对源数据进行影响。

2、stream提供的新方法

 

3、创建Stream的三种方式

public class StreamAPITest {
    //创建Stream的方式1:通过集合
    @Test
    public void test1(){
        List<Employee> list = EmployeeData.getEmployees();
        Stream<Employee> stream = list.stream();
    }

    //方式2:通过数组 调用Arrays类的static <T> Stream<T> stream(T[] array): 返回一个流
    @Test
    public void test2(){
      Integer[] arr=new Integer[]{1,2,3,4,5};
        Stream<Integer> stream = Arrays.stream(arr);
    }

    //方式3:通过Stream的of()
    @Test
    public void test3(){
        Stream<String> stream = Stream.of("AA", "BB", "CC");
    }
}

JDK8之后的新特性

1、使用record不能在声明类中定义实例字段,类不能声明为abstract(因为目的就是为了声明一个类),不能声明显式的父类(因为它已经有一个父类了,是Record,类的继承是单一继承,即只能有一个直接父类),不能设置实例变量属性等。

 2、JDK8的新特性(相较于JDK7),最可以谈的有lambda表达式和Stream API

  和JDK7的对比:包括元空间、HashMap、新的日期时间API等

 

3、HotSpot虚拟机来说 jdk7:方法区的落地体现:永久代

           jdk8:元空间。

解释:永久代和元空间都属于方法区,都是方法区的实现。

     元空间:使用本地内存,它不属于JVM内存,不受JVM垃圾回收的控制。最大大小取决于系统内存,而不是堆大小

   永久代:使用JVM的内存。

使用元空间的原因:避免OOM(OutOFMemory),使用永久代需要默认设置永久代的初始大小和最大值,但并不是总能知道设置的值应该是多少,所以就会报OOM错误。而元空间使用本地内存,本地内存有多大就可以用多少,默认为无限。

posted @ 2024-12-02 11:01  Dyj07  阅读(5)  评论(0编辑  收藏  举报