黑马程序员-Java基础总结15——高新技术Day01

时间:2023-02-18 14:10:02

高新技术Day01

-------android培训java培训、期待与您交流!---------- 

内容:   MyEclipse小技巧、JDK1.5新特性、享元模式枚举Enum反射Reflect反射类体系(Class、Field、Method、Constructir等)、数组反射应用、hashCode与equals作用、框架(Framework)。

一、MyEclipse相关知识:

1、在MyEclipse下如何查看一个变量的值(调试)

    先在需要查找的字符所在行前方双击(即平时提示错误的那个竖列,实现“断行)

右键Debug as,进入Debug透视图,在Expressions视图添加查看的变量name(:intxx),之后通过左边的Debug视图的Resume(F8)等键控制代码执行,可观察Expressions视图下变量值的变化

2、显示需要的视图:

Window——>Show View——>...........(根据需要选择,要学会揣测)

3编译器(javac)与运行器(java)的版本最好统一,而编译器版本不能高于运行器的版本,否则发生UnsupportedClassVersionError异常(类文件主辅版本号不支持)。

Java源文件(.java) ———> 类文件(.class) ———> 内存中的字节码———> .....

                  编译                 类加载器               运行

4、可设置整个工作间或单个工程的javacjava的版本,即类似Java编程思想,继承父类,但可复写父类中方法(呵呵,MyEclipse也是用Java编写的嘛)

5、编程时需要使用到jar包,通常MyEclipse都有默认的jar库,如果需要可以添加或修改jar文件,或修改、启用其他的库或者自定义库(自行添加jar文件到库中)

6MyEclipse中的快捷小操作:

A、自动生成类的构造函数: 

右键——>Source(或Alt+Shift+S)——>Generate Constructor usingFields

2、自动生成类中的getset()方法(获取、修改成员变量):

右键——>Source(或Alt+Shift+S)——> Generate Getter andSetter;等

3、打开运行对话框————> Arguments Program arguments 输入需要main方法的String[] args 传值String内容。(相当于java类名String1 String2....)

 

————————————基础知识加强、补充————————————

一、 JDK1.5新特性:

1、静态导入: 

   import java.util.Collections.*; //静态导入指定类中的指定静态方法;

作用: 可以减少类名调用静态方法时类名的书写; 例: sort(ArrayList al);

2可变参数: VariableParameter

public void sort(String str,int ...args)

调用格式: ...add("hehe",32,23,4,33);...add("hehe",32,23);

   定为可变参数的类型元素相当于数组,例如:int[] args

——————知识点插曲——————

重载与重写的区别等:

Overload(重载):一个类中存在多个同名方法,但是只要它们的签名(接收)参数列表不同,Java就会将它们看做唯一的方法。这样两个参数列表不同的同名方法就称之为重载方法。

Override(重写):子类继承父类方法,参数列表与返回值类型完全相同,但是方法体内容(即方法执行代码)不同,这样就成为重写或覆盖父类方法。

Refactoring(重构):就是在不改变软件现有功能的基础上,通过调整程序代码改善软件的质量、性能,使其程序的设计模式和架构更趋合理,提高软件的扩展性和维护性。

PS: 重构这不是Java特性,而是MyEclipse等工具的功能,例如: 可改变所有使用到某个变量的.java文件中该变量名或通过改变某个变量的参数类型使对应的Java类使用新的参数Java类,实现更新升级等。

简单概括:

1、重载: 存在于同一个类中,只看同名函数的参数列表

2、重写: 存在于子父类间,子父类方法(参数列表与返回值类型)一样,方法体内容不同

3、重构: 软件程序的内部结构的调整,是实现“事先设计”的基础

————————————

 

3、高级for循环【详见集合篇之高级for循环】

for(final String s : str){    //高级for循环之前是可以添加修饰符的;

   //遍历执行代码;    }

4、基本数据类型对象的自动装箱和拆箱: 

字节byte: -128——127

设计模式之“享元模式(Flyweight Pattern):

  1、将基础、常见通用的元素进行共享,合并为一个对象;

  2、不同的东西就整合成为外部属性,作为方法的参数传入)。

解析:   (理解,可略)

 flyweight是英语拳击举重中最轻量级的意思,在Java中表示细粒化或最小的元素/对象,例如: 英文字符或可用单字节存储的阿拉伯数字等。

  这些字符与数字在软件程序中会被大量使用,而使用多次同一字符时是需要在内存中新建该字符对象(:匿名对象),因此通过将这些使用频繁且细粒化的对象变成共享数据/元类,重复使用字符时就相当于是调用共享区中的引用。因此将这种处理方式称之为享元模式。

【代表:java中如果用到1字节(byte)以内的数字 它会把他缓存起来进行复用,即使是newInteger对象也是指向相同的地址值。】

内部状态与外部状态:

将小对象(26字母、-128127之间数字、char等常见关键字,应该就放在静态区)

而不同的东西变成方法(参数)属性,是外部状态,而那些小对象组成的(整个单体)就是内部状态。

示例比喻: 游戏中的小兵,兵的模型等共享为对象,而血量变化等属性不是共享的就可以定为外部属性(同理还有电脑中的文件夹等)

———————————————枚举(Enum)———————————————

二、枚举: Enum,就是一个特殊的类;

枚举符号: enum;  

: 枚举的元素列表(静态变量/常量)必须处在该枚举类第一行

     枚举的成员就是本类的实例化对象

其他:

1类的方法返回值类型可以是本类对象  public People getWife(){ return …… }

2、类中可定义静态常量,例如Math.PI(即π)Integer.MAX_VALUE(int型最大值2^31-1)

当静态常量的类型是本类类型指向的是本类实例对象时,枚举可简化书写

 

枚举(java.lang.Enum):      java.util.Enumeration接口】

一般方法:

toString()  :返回枚举常量名(字符串形式返回)

ordinal()  :返回enum声明中枚举常量的位置(角标)

getDeclaringClass()  :返回与此枚举常量的枚举类型相对应的Class 对象;

【不同于Object.getClass(),getClass()方法返回值带有枚举常量(枚举角标等)

静态方法:

valueOf(String str)  :根据字符串获取对应的枚举值;(即逆向的toString方法)

values()  :返回包含全部枚举值的数组;

 

解析:  (知识水平有限,而我对枚举的认识就是枚举就是为了简化书写而产生的,常用枚举实现的功能,其实一般类都能实现)

()为避免思路错乱,先整理下知识:

创建类的实例化对象的方式:

1、常见new 对象:   Date date = new Date();【适用于该构造函数为默认或public权限】

//以下两种属于私有化构造函数的情况;

2通过方法获取实例化对象: 该类自身提供的静态方法存在于外部类中的一般方法(例: 集合的迭代器Iterator)

3通过类类型的静态变量来获取:  

   public final static Traffic RED = new Traffic("Red");

     或使用JDK1.5版本后提供的枚举Enum来实现(简化上面的代码书写): RED("Red");

调用:  Traffic red =Traffic.RED , 即可获取到该静态类变量对应的类实例化对象。

 

()枚举的特点/优点:  

1、简化获取构造函数的代码书写 (枚举值)

即简化静态常量(变量)的类型是本类类型且指向的是本类实例对象时的代码书写。

2、注意:枚举属于饿汉式,一进内存就会加载

(例如: 当加载枚举的类文件时,构造函数存在打印语句,在加载时就会直接打印输出)

3、提示:

饿汉式的单例(设计模式),可以用只有一个成员(枚举值)的枚举来实现,可简化代码。

 

———————————————反射(Reflect)——————————————

三、反射:Reflect

基石: ——> Class

   Class cls = String.class;  (字节码)

可理解为:  (在写程序时经常会做下面动作)

import java.util.TreeSet;  //导入TreeSet类的字节码文件到内存中;

理解:  因为当我们使用到TreeSet集合时,也就是用到TreeSet类和其中的方法,而JVM在运行程序时要在内存中加载字节码文件,因此就需要加载TreeSet类的字节码文件,才能读取到其中的方法等。

【所以在写程序时要尽可能明确调用的类,尽量不要"java.util.*;",因为这会在内存加载整个util包中的类的字节码文件,也就会占用多余的内存。】

Java程序在加载时至少要加载哪些字节码文件?————

                               整个java.lang包和主函数的所在类的字节码。)

联系理解:

人以及人的属性、行为————>Person

Java类以及变量、方法————>Class

                      Java类与反射体系图

           【PS: 下文会对反射体系中各成员进行详解】

黑马程序员-Java基础总结15——高新技术Day01

(一)获得Class类的对象:

A类名.class;      Date.class;

B对象.getClass();     new Date().getClass();

CClass.forName("java.util.Date");【反射应用的基础,将字符串的值定义成变量】

 

【举例帮助理解:

以泛型适配符T为例,Collection :定义该集合要接收一种数据类型,但是接收类型不明确,为了提高扩展性,就将接收的数据类型定为变量T,实际使用时传入什么数据类型就需要对变量T进行赋值,因此就可根据变量T去调用所需要的数据类型(的字节码),而这个调用的过程就相当于反射。

也就可理解Class.forName()为什么是反射主要的应用方式,因为调用数据类型不明确】

 

(二)返回Class(字节码)的两种方式:

A、该字节码文件已在内存中存在直接调用并返回该字节码(上面的三者均可)

B、该字节码未在内存中存在加载该字节码文件然后返回该字节码(通常是第三种)

(因为通过对象调用或类名调用,则该类的字节码肯定已经在加载到内存)

 

(三)九种预定义的实例对象(Class对象):  (见PPT

1void.class;   void也是基本数据类型,只不过返回值为空。)

28个基本数据类型(booleanint)

解析(个人观点,仅供参考):

在编写Java程序时常用到的是java.lang包中的类以及8个基本数据类型与void(9个好像没有在API中占一席地,也就不包括在lang),因此加载时除了lang包,也就要有预定义的实例对象。

Void等类的TYPE是对应这9个预定义的class对象,仅供理解。】

对应如下: (获取基本数据类型字节码的方法)

void.class == Void.TYPE;true),而void.class == Void.class; (false)

(即: 8种基本数据类型以及void,在Java中可通过八种基本数据类型包装类的静态常量TYPE获得对应字节码。)

 

(四)其他:

1、数组类型的Class实例对象:  数组.class

2Class类方法:

    .isPrimitive()  :  是否为基本数据类型

 【仅基本数据类型(int).class、基本数据类型对象(Integer).TYPE;> true

    .isArray()  :  是否为数组类型;

 【基本数据类型数组: int[].class.isArray();> true

T   .newInstance()  :创建此Class 对象所表示的类的一个新实例;

 【即返回不带参数的构造方法,如果该类尚未初始化,则初始化这个类。

Constructor.newInstance(Object...initargs)返回(可变参数列表的)构造方法】

 

————————————反射类———————————

四、反射类体系:

Java中,将一个Java类用Class类的对象来表示,而该Java类中的变量、方法、构造方法、包、修饰符等信息,就是用相应类的实例对象来表示: FieldMethodConstructorPackage等等。

   【总结:反射就是把Java类中的各种成分映射成相应的java。】

注意:

1、程序开发时要明确代码编译和运行的执行范围(编译时异常、运行时异常)

2、反射的缺点: 会导致程序性能严重下降(运行速度有所下降,相对计算机运行效率而已)

【建议:当重复反射调用某个类时,应该根据情况将反射获取到的字节码进行缓存,减少重复反射的动作等】

 

理解:    为什么Class类是反射的基石?

1、因为每个类被加载到内存时,都会被加载字节码class(该字节码唯一,且包含该类的属性信息: 包名、成员变量、方法等)

2、通过反射获取到该字节码class属于Class类,而Class类中存在获取其他类中的属性信息的方法(类似集合中存在的iterator()方法)

3、通过这些getMethodgetField()等方法,可以对其他类中的属性、行为等进行操作、修改等(这就是反射的应用)

———————————————反射中的成员类——————————————

五、反射中的成员类:

(一)Field:(成员变量)

1、创建Field对象的Class类方法:

.getField(String name)  :访问该类变量,即返回Field对象(但只能返回非private变量)

.getDeclaredField()  :访问该类中声明的(成员)变量(可访问private变量)

Filed[]  .getFields()   :返回该类所有的非private的成员变量。

 

2Field类的方法:

get(Object obj)   :返回指定对象上此Field表示的字段的值;

【继承java.lang.reflect.AccessibleObject的方法:

  .setAccessible(true);:使指定的Field对象变成可访问;

(: 用于getDeclareField方法返回的private成员变量,使其可被get(obj)方法获取。)

  .setAccessible(AccessibleObject[] array,boolean flag) :

      同上,区别: 可对Field[]Method[]Constructor[]进行“权限批量处理”;

———————————————

(二)Method: (方法类)

1、创建Method对象的Class类方法: getMethod("MethodName",Class... c);

2Method类的方法:     (成员方法的反射)

.invoke(原来调用的对象,方法中的参数): (反射)调用指定的方法;

3、代码示例:

通常方式:  String str = "abc";  str.charAt(2);
反射方式: Method methodCharAt =String.class.getMethod("charAt",int.class);
methodCharAt.invoke(str,2);


4、注意:

A因为静态方法不需要指定调用方法的对象,所以当调用静态方法时可以"原来参数"可定义为null,即不需要对象就能调用的方法(类名调用)

 

B疑惑:method.invoke(main.class,(Object)new String[]{"hehe","xixi"});

解析: 需要添加(Object)或用new Object[]{...}包装的原因

Method类中的方法: .invoke(Object obj,Object...args)由于Object...args兼容Object[]数组,所以当传入值类型属于Object[]或其子类时会被拆包

————————————

 

(三)其他成员获取方法:  【其他方法详见APIjava.lang.Class类】

Class<?>[]   getClasses();   :返回一个包含某些Class 对象的数组;

  【返回的对象表示属于Class对象中的类的成员的所有公共类和接口

 

Constructor<T>   getConstructor(Class<?>...parameterTypes) :

    返回Constructor对象,反映该Class对象所表示的类的指定公共构造方法

Package   getPackage()   获取此类的包;

ClassLoader   getClassLoader()   返回该类的类加载器

 

(其他)面试题:  如何利用反射实例化一个类?

1、如果使用无参的构造函数,直接使用Class类的new Instance()方法即可;

2、若需要使用特定的构造函数来创建对象,则需要先获取Constructor实例,再用new Instance()方法创建对象

 

——————————————数组反射应用——————————————

六、数组反射:

1、int[]等是基本数据类型数组(属于Array类,数组),也就是说整个int[]等是Object对象,是可以调用Object类的方法等。


2、打印数组中元素的方式(只能打印对象类型数组):

通过Arrays.asList()方法将数组变成集合,而sop(集合)时能明了查看到集合中的元素

PS:不能实现对int[]等基本类型数组转换查看的原因是转换时将整个int[]数组作为一个对象存储到集合中,所以打印出来的依旧是基本类型数组的地址值。


3、遍历数组的新方式(通过反射实现):

String[] obj = new String[]{"hehe","xixi","haha"}; 

   (调用:) sortArray(obj);

//以反射形式遍历数组的方法:

private static void sortArray(Object obj){

   int len =Array.getLength(obj);

   for(int x=0; x<len;x++)

   System.out.println(Array.get(obj,x));  }

  //适用于:不明确要遍历数组的数据类型


4、无法获取整个数组的元素数据类型,只能通过数组中某个元素获取其对应数据类型(通过字节码来获取类型)

:   Object[] obj = new Object[]{"abc",22,''};

      obj[2].getClass().getName();   (单个元素的数据类型)

有点烂的例子:  int[] i = new int[3];//无法通过数组名i,来获取数组对象的类型;



(其他)英文异常提醒:

Unhandled exception(没有处理异常) typeClassNotFoundException(类找不到异常) 

 ————————————字节码间的比较(注意,小细节)———————————

七、字节码之间的比较:

1、基本数据类型(int)只能用类名获取字节码,而基本数据类型对象与基本数据类型数组等可通过类名或变量名获得对应的字节码

 

2、数据类型(基本、引用与数组数据类型)间字节码比较符使用:

  JDK1.5版本开始,只有同种数据类型间(intStringString[])可用"=="号来判断两个字节码文件是否相同;equals()方法适用于同种或不同种数据类型间(或者不明确的情况)

【特殊:基本数据类型与对应的基本包装类的class字节码可用"=="号进行比较,但是基本数据类型数组与对应的包装类对象数组则不能用"=="号进行比较。】

 

3、类型提升比较:  当方法接收类型参数或以多态形式赋值时,父类变量调用方法获得的字节码可与其子类字节码用"=="进行比较

理由:由于类型提升与Java编译检测方式(水平有限,说不清,偏向前者多一点).

:  Number num = new Long(33);  //多态形式赋值

   System.out.println(num.getClass()==Short.class); //编译通过

字节码"=="比较编译解析:

sop(num.getClass()==String.class); //无法通过,String不是Number子类

sop(Number.class==Long.class);     //无法通过,不同种数据类型

sop(new Long(33).getClass()==Number.class);  //无法通过,同上;

数组数据类型也适用,不过也受第2条中“特殊”情况的限制。】

 

4、通过反射获得class类型可以任意类型进行比较(理由如下)

  编译与运行时检测解析:由于Java在编译时会检测类型限定(或代码规范等),而运行时则不检测代码的定义的限定

  因此当通过反射获得class类型在编译不能判断具体类型,而在运行时没有类型限定,因此可以与任意类型进行比较。

: class string:  public String str ="hehe";

   其他类:  Field field =string.getClass().getField();

       field.class ==int.class; (编译可通过)

同理: 因为Java泛型是伪泛型(编译后会类型擦除),所以也可通过反射来给带泛型的集合添加任意有效元素。】

 

5、警惕: 创建类的对象,如果值为null,那么无法通过对象引用获取字节码文件,而且编译会通过,但是会在运行时抛出NullPointerException

例如: String s = null;    System.out.println(s.getClass());

  【上述代码编译通过,但运行时抛出NullPointerException异常】

——————————————————————————————

八、hashCodeequals的作用:

hashCodeequals的关系与作用:   【详见之前的集合篇】

1、存储到hashSethashMap等集合/映射中的元素对象应该复写hashCode()equals()方法,使元素对象具备比较性;

2、存储到hashSet集合时先比较两个对象hashCode值,如果相同再用equals比较。

【因此两个对象的equals相同时,它们的hashCode值必定相同;反之则不一定。

 

特殊知识点:  (注意)

   当一个对象存储到HashSet集合(HashMap)中以后,就不能修改该对象中那些参与计算hashCode(哈希值)的字段,否则,对象修改后的哈希值与最初存储时的哈希值不同,造成对象的哈希值无效化(个人观点);因此:

1、无法对该对象进行删除等依靠hashCode值比较判断的操作

2、修改后该对象的哈希值无效,也就无法参与比较,当存储元素内容相同(哈希值相同)的元素时,可以被存储进该集合,导致HashSet(HashMap)中的元素不唯一

3当对集合中的多数对象进行相同操作时,由于旧的元素没有在内存中删除,不断占用内存,加剧内存泄漏;当存在大规模该问题时甚至导致内存溢出(异常)

【提醒:注意此情况,可联系实际,在解答或使用hashCode时注意/提及内存泄漏问题,当然也可以用此例子作为内存泄漏的案例。】

名词解析:

   1、内存泄漏(memoryleak)由于疏忽或错误造成程序未能释放已经不再使用的内存的情况,是应用程序分配某段内存后,由于设计错误,失去了对该段内存的控制,因而造成了内存的浪费。(一般常说的内存泄漏是指堆内存的泄漏。)

   2、内存溢出是指应用系统中存在无法回收的内存或使用的内存过多,最终使得程序运行要用到的内存大于虚拟机能提供的最大内存。(通俗理解就是内存不够;例:通常运行大型软件或游戏时所需要的内存远超出主机中的内存所承受大小,就叫内存溢出。)

————————————————框架(Framework)——————————————

九、框架(Framework):

框架:    构建的类调用其他类;

工具类:  构建的类被其他类调用。

以房子为例,房屋即为一个框架,它有提供一个安装门的位置,等待住户根据需要安装不同类型的门(门亦为工具类,被选择调用),而房屋调用门,使其具备门的特有属性(防盗等)

 

Java三大框架: (SSH)

Spring: J2EE框架,是轻量级的控制反转(IoC)和面向切面(AOP)的容器框架。

Struts: Web框架,采用JavaServlet/JSP技术,实现基于JavaEE Web应用的MVC设计模式的应用框架【MVC:即模型(Model)-视图(View)-控制器(Controller)】。

Hibernate: 持久层框架,即:JDBC(Java数据库连接)轻量级封装。

通俗总结:简称SSH,其中Structs主要用来做表现层的,Hibernate是用来做DAO(数据访问)层的,而Spring是用来联系起两者的。

 

框架的简单构建:

1、创建配置文件:(config.properties)用于存储所需的程序调用属性信息;

2、构建框架类:通过IO流或类加载器读取配置文件中的信息,将信息资料通过反射转换成类实例对象等,从而去执行指定的操作动作与行为。

功能作用: 通过反射提高框架的扩展性,通过直接对配置文件的修改可以变更运行程序中的指定内容代码。

(: 修改反射集合,*选择存储集合为ArrayListTreeSet等,或者修改调用类、接口,选用不同功能或升级后的类程序)

 

备注:

1Properties映射(可从流中读取或保存数据的Hashtable的子类,键值均为字符串)

2、关闭close流的原因: 要养成及时关闭IO流的习惯,当程序为多线程、长时间运行时,如果多次创建流均没有关闭,会不断占内存空间,由于JVM需要流长时间空闲后才会将其进行清理),所以当程序运行到一定程度时,可能会发生内存不足,导致内存溢出。

3、配置文件的路径设置:

//getRealPath()/内部位置;  : 软件等文件安装位置+相对路径

  读取配置文件的路径一定要是完整的路径,但完整的路径不是硬编码,而是演算出来的(根据getRealPath()【文件安装位置】来组建完整的路径)

4、读取配置文件的方式:

A、IO,可读可写,但配置路径难以与调用的Java程序关联一致(因此仅使用写入功能)

B、调用的Java源文件的类加载器的getResourceAsStream()方法加载(可使用相对路径,注意:类加载器只能加载文件信息,不能写入数据)

C、建议:Java源文件的getResourceAsStream()方法加载(底层是类加载器,简化了代码,可使用相对或绝对路径,但绝对路径是基于文件安装位置起的)

【示例: 文件安装位置: FilePath =D:/JavaDemo

完整路径: FilePath/com/DemoDay/java.properties

相对路径: java.properties (DemoDay目录为调用源程序的当前目录)

绝对路径: /com/DemoDay/config.properties 

 

5、其他:

1)框架的配置文件存储在classPath路径下,因为框架加载配置文件方式是类加载器加载

2)软件开发中的三种级别重用:

A内部重用,即在同一应用中能公共使用的抽象块;  【例: 封装遍历集合功能的方法】

B代码重用,即将通用模块组合成库或工具集,以便在多个应用和领域都能使用;

  【即工具、或多个工具组成工具集/库,工具亦为封装多个公共方法的类】

C应用框架的重用,即为专用领域提供通用的或现成的基础结构,以获得*的重用性。  

【实例为框架,例:Work软件等,提供框架平台,使用者根据需要选择工具等来添加文字或图片等,实现自己所需的结果】

3)框架与设计模式:

框架:可以是调用其他类的类或软件等,一个框架中往往含有一种或多种设计模式;

设计模式:是对在某种环境中反复出现的问题以及解决该问题的方案的描述,它比框架更抽象,可以看做是软件的知识。