Java基础加强学习
1. 了解IDE
IDE其实就是集成开发环境。
2. 静态导入
import语句可以导入一个类或者某个包中的所有类。
import Static语句导入一个类中的某个静态方法或者所有的静态方法。
//直接导入方法
Import staticjava.lang.Math.max;
//静态类下的方法都导入
Import staticjava.lang.Math.*;
3. 可变参数
主要涉及到的是方法覆盖和方法重载。
一个方法接收的参数个数不固定,例如:
System.out.println(add(2,3,4));
System.out.println(add(2,3,4,5));
Override和overload的区别:
Overload是重载,同一个函数可以有不同的参数列表。
Override是覆写父类的方法。
可变参数的特点:
只能出现在参数列表的最后;
…位于变量类型和变量名之间,前后空格可选;
调用可变参数的方法时,编译器为该可变参数隐含创建一个数组,在方法体中以数组的形式访问可变参数。
示例:
class VariableArgs
{
public static int add(int x, int ... args)
{
//把args当作int数组就可以了
int sum = x;
for(int a:args)
{
sum += a;
}
return sum;
}
public static void main(String[] args)
{
System.out.println(add(1,2,3,4,5,6));
}
}
4. 增强for循环
增强for循环的语法格式:
for(type 变量名:集合变量名)
{}
注意事项:
1.迭代变量必须在{}中定义。
2.集合变量可以是数组或实现了Iterable接口的集合类。
举例:
public Static int add(int x,int…… args)
{
int sum=x;
for(int arg:args)//此处就是增强for循环的格式
{
sum+=sum;
}
return sum;
}
5. 基本数据类型和自动装箱与自动拆箱
自动装箱
Integer num1=12;
自动拆箱:
system.out.println(num1+12);
基本数据类型的对象缓存:
Inter num1=12;
Inter num2=12;
System.out.println(num1=num2);
Integer i1 = 5;
Integer i2 = 5;
System.out.println(i1==i2); //true
Integer i3 = 128;
Integer i4 = 128;
System.out.println(i3 == i4); //false
Java将小于一个字节的数字(-128~127)放在内存中常驻。而对于大于一个字节的才在堆中创建。(python中也用到此技术)
享元模式(flyweight)
多个小的对象,内容/属性大部分相同。可以将其统一设计为一个对象,相同的部分作为内部状态,不相同作为外部状态内容。其他不同的部分通过参数传入来设置。
Integer.valueOf(int) //将int显式得转为Integer
Integer.valueOf(5) == Integer.valueOf(5) //true
6. 享元模式:flyweight pattern
简而言之就是共享物件,用来尽可能减少内存的使用量。
7.枚举
什么是枚举?
枚举是一种特殊的类。
枚举一般是结合内部类出现的。同时枚举一般是用来表示那些确定的元素。比如星期几,性别等。枚举的对象是确定的,类或者方法调用不能创建新的,只能调用枚举类中已经存在的。
枚举就是要让某个类型的变量的取值只能为若干个固定值中的一个,否则,编译器就会报错;枚举可以让编译器在编译时就可以控制源程序中填写的非法值,普通变量的方式在开发阶段无法实现这一目标。
用普通类如何实现枚举功能,定义一个Weekday的类来模拟枚举功能
私有的构造方法:
每个元素分别用一个公有的静态成员变量表示
可以由若干个公有方法或抽象方法,例如:要提供nextDay方法必须是抽象的
采用抽象方法定义nextDay将大量的if else语句转移成了一个个独立的类(使用内部类)
枚举的基本应用:
举例:定义一个WeekDay的枚举
扩展:枚举类得values.valueOf.name.toString.final等方法
总结:枚举是一种特殊的类,其中的每个元素都是该类的一个实例对象。例如可以调用WeekDay.SUN.getClass.getName和WeekDay.getClass.getName
枚举就相当于一个类,其中也可以定义构造方法,成员变量,普通方法和抽象方法
枚举元素必须位于枚举体中的最开始部分,枚举元素列表的后要有分号与其他成员分隔。把枚举中的成员方法或变量等放在枚举元素的前面,编译器报告错误。
带构造方法的枚举
构造方法必须定义成私有的
如果有多个构造方法,该如何选择哪个构造方法?
看元素初始化了MON()和MON(1)调用不同的构造方法
枚举元素MON和MON()的效果一样,都是调用默认的构造方法
带方法的枚举
定义枚举trafficLamp
实现普通的next方法
实现抽象的next方法:每个元素分别是由枚举类得子类来生成的实例对象,这些子类采用类似内部类的方式进行定义
增加上表示时间的构造方法
枚举只有一个成员时,就可以成为一种单例的实现方式。
name() //得到枚举值的名称
ordinal() //得到枚举值在枚举声明中的位置
static EnumClass valueOf(String name); //得到枚举名称为name的枚举值
static int[] values(); //获得枚举对应的所有值组成的数组。
枚举类型在switch中不需要枚举名,只需要枚举值:
Weekday w = Weekday.MON;
switch(w)
{
case(MON):
…
Break;
……
}
带有构造器的枚举
public enum WeekDay
{
/*
元素相等于静态的成员,没有括号的时候调用默认构造方法,也可以指定调用其他构造函数。
*/
SUN(0),MON(1),TUE(2),WED(3),THI(4),FRI(5),SAT(6);
//元素列表必须在枚举类最前面。
//构造函数必须是私有的。
private WeekDay(int day)
{}
}
带有抽象方法的枚举
内部类有四种访问权限,而普通的类只有public和包访问权限。
public class Weekday1
{
public static void main(String args[]){
//调用父类有参数的构造方法:
new Date(300){};
}
public enum TrafficLamp{
RED(30){
public TrafficLamp nextLamp(){
return GREEN;
}
},
GREEN(45){
public TrafficLamp nextLamp(){
return YELLOW;
}
},
YELLOW(5){
public TrafficLamp nextLamp(){
return RED;
}
};
public abstract TrafficLamp nextLamp();
private int time;
private TrafficLamp(int time){
this.time = time;
}
}
}
上面中的枚举类型中会编译出三个class文件。
如果枚举只有一个成员时,可以作为一种单例模式的实现方式。
7. 反射
反射的基石:Class类
Class-à代表Java中各个java类,描述java类的相关属性:类名/父类/所在包等等
Class类对象包含其他类的字节码信息。每个类对应一个字节码文件。
Date.class //得到Date类字节码
对象.getClass(); //得到对象所属类的字节码
Class.forName(“java.lang.String”); //得到指定类的字节码
其工作情况分两种:已载入内存的字节码和未载入内存的字节码
前者可以直接获得内存中的字节码,后者则会调用类加载器加载类,再获得其类字节码。
上面就是三种获得类字节码的方法,只要是同一个类,那么得到的最终是同一个字节码。
其中反射的话通常是用Class.forName方式,因为运行时不知道类名。
Class.forName还会抛出ClassNotFoundException.
9个预定义的Class对象(一开始就载入虚拟机):8个基本数据类型和void
示例:
publicclass ReflectTest
{
public static void main(String[] args)throws Exception
{
String str = "123";
Class cls1 = str.getClass();
Class cls2 = String.class;
Class cls3 = Class.forName("java.lang.String");
System.out.println( cls1 ==cls2);
System.out.println( cls1 ==cls3);
System.out.println(cls1.isPrimitive());
System.out.println(int.class.isPrimitive());
//false
System.out.println(int.class== Integer.class);
//true:Integer:public static finalClass<Integer> TYPE
System.out.println(int.class== Integer.TYPE);
//false
System.out.println(int[].class.isPrimitive());
//true
System.out.println(int[].class.isArray());
}
}
什么是反射?
反射就是把java类中的各种成分映射成相应的java类。
如何得到各个字节码对应的实例对象(class类型)
1.类名.class,例如:System.class;
2.对象.getclass(),例如:newDate().getclass();
3.class.forName(“类名”),例如:class.forName("java.util.Date");
Constructor类
1.得到某一个类所有的构造方法:
例子:Constructor[]constructor=Class.forName(“java.lang.string”).getConstructor
2.得到某一个构造方法:
例子:Constructorconstructor=
Class.forName(“java.lang.String”).getConstructors(StringBuffer.class)
获得方法时要用到类型
3.创建实例对象:
一般方式:Stringstr=new String(new StringBuffer(“abc”));
反射方式:Stringstr=(String)Constructor.newInstance(new String Buffer("ABC");
调用获得的方法时要用到上面相同类型的实例对象。
Class.newInstance()方法:
例子:String obj = class.forName(“java.lang.String”)newInstance();
该方法内部先得到默认的构造方法,然后用该构造方法创建实例对象
该方法内部的具体代码是怎么写的?用到了缓存机制来保存默认构造方法的实例对象。
Field类反射
Field类代表某个类中的一个成员变量
需求:将任意一个对象中的所有String类型的成员变量所对应的字符串内容中的“b”改成“a”。
字节码的比较,使用 = =,不要使用equals;(因为这里只是一份字节码,没有第二份,所以不用equals)
代码:
String str1="abc";
Class cls1 = str1.getClass();
Class cls2 = String.class;
Class cls3 = Class.forName("java.lang.String");
System.out.println(cls1==cls2);
System.out.println(cls1==cls3);
System.out.println(cls1.isPrimitive());
System.out.println(int.class.isPrimitive());
System.out.println(int.class==Integer.class);
System.out.println(int.class==Integer.TYPE);
System.out.println(int[].class.isPrimitive());
System.out.println(int[].class.isArray());
//记住1.强转 2.参数类型必须一致
//Constructor类反射
Constructor constructor = String.class.getConstructor(StringBuffer.class);
String str2 = (String)constructor.newInstance(newStringBuffer("abcd"));
System.out.println(str2.charAt(3));
//Field类反射
//field不是对象身上的变量,而是类上的,要用它去取某个对象身上的值
ReflectPoint pt1 = new ReflectPoint(3,5);
Field fieldy = pt1.getClass().getField("y");
System.out.println(fieldy.get(pt1));
Field fieldx = pt1.getClass().getDeclaredField("x");
fieldx.setAccessible(true);//暴力反射
System.out.println(fieldx.get(pt1));
changeStringValue(pt1);
System.out.println(pt1);
//str1.charAt(1);
Method methodCharAt = String.class.getMethod("charAt",int.class);
System.out.println(methodCharAt.invoke(str1, 1));
}
private static void changeStringValue(Objectobj) throws IllegalArgumentException, IllegalAccessException {
Field[] fields = obj.getClass().getFields();
for(Field field : fields)
{
if(field.getType()==String.class)
{
String oldValue = (String)field.get(obj);
String newValue = oldValue.replace('b', 'a');
field.set(obj, newValue);
}
}
}
8.Method类代表某个类中的一个成员方法
得到类中的某一个方法:
例子:MethodcharAt=class.forName(“java.lang.String”)
getMethod("charAt",int class);
调用方法
一般方式:System.out.println(str.charAt(1));
反射方式:System.out.println(charAt.invoke(str,1);
如果传递给Method对象的invoke()方法的一个参数为null,说明该Method对象对应的是一个静态方法。
JDK1.4没有可变参数,因此invoke中使用Object数组作为参数。
JDK1.5向下兼容
示例:
import java.lang.reflect.*;
class TestArgs
{
public static void main(String[] args)
{
for(String str: args)
{
System.out.println(str);
}
}
}
public class ReflectTest
{
public static void main(String[] args) throws Exception
{
//TestArgs.main(newString[]{"111","222"});
//String startClassName =args[0];
Method mainMethod =Class.forName(args[0]).getMethod("main",String[].class);
//编译报错,mainMethod.invoke(null, newString[]{"111","222","333"});
//一个Object数组,只有一个元素,这个元素是一个String[]数组
mainMethod.invoke(null, newObject[]{new String[]{"111","222","333"}});
//跟编译器打招呼:我这个String[]组你就当作一个Object得了。
mainMethod.invoke(null,(Object)new String[]{"111","222","333"});
}
}
当使用mainMethod.invoke(null, newString[]{"111","222","333"});时,java编译器编译报错,原因是JDK1.4中有invoke(Object[])的方法。为了向下兼容,JDK1.5将newString[]{"111","222","333"}作为一个Object数组来处理,即分拆为三个参数传给函数。
警告: 最后一个参数使用了不准确的变量类型的 varargs 方法的非 varargs 调用; mainMethod.invoke(null, new String[]{"111","222","333"});
对于 varargs 调用, 应使用多个Object分开,对于非 varargs 调用, 应使用 Object[], 这样也可以抑制此警告。
为此可以使用下面两个方法:
mainMethod.invoke(null, newObject[]{new String[]{"111","222","333"}});
mainMethod.invoke(null,(Object)new String[]{"111","222","333"});
9.数组的反射:
具有相同维数和元素类型的数组属于同一类型,即具有相同的class实例对象。
代表数组的class实例对象的getSuperClass()方法返回的父类为Object类对应的class。
示例:
public class ReflectTest {
public static void main(String[] args)
{
int[] a1 = new int[3];
int[] a2 = new int[4];
int[][] a3 = new int[3][4];
String[] a4 = new String[4];
/*
System.out.println(a1.getClass()== a2.getClass());
System.out.println(a1.getClass()== a3.getClass());
System.out.println(a1.getClass()== a3.getClass());
*/
//维数相同
//System.out.println(a1.getClass()== a2.getClass());
//维数不同连编译都报错了
//System.out.println(a1.getClass()== a3.getClass());
//输出为[[I
System.out.println(a3.getClass());
//compile error System.out.println(a1.getClass() == a4.getClass());
}
}
补充知识点:int不是一个Object,但是int[]是一个Object。
int[] a1 = new int[3];
int[] a2 = new int[4];
int[][] a3 = new int[3][4];
String[] a4 = new String[4];
System.out.println(a1.getClass().getSuperclass().getName());
System.out.println(a4.getClass().getSuperclass().getName());
Object aObj1 = a1;
//a1是int数组,里面每个元素是int,不是objcet,不能够Object[]aObj2 = a1;
Object aObj3 = a3;
//a3是二维数组,里面每个成员是一维int数组。一维int数组是Object
Object[] aObj4 = a3;
//都是打印出 类名@hashCode
System.out.println(a1);
System.out.println(a3);
System.out.println(a4);
//使用asList打印。JDK1.4使用Object数组
//由于a1是int数组。它不能当作一个Object数组,只能将其当作一个Object
//所以打印Arrays.asList(a1)还是得到一个只有一个元素的List。
//Arrays.asList接受可变参数,同时还有1.4以前的接受数组的asList
System.out.println(Arrays.asList(a1));
//a3转为一个List,每个元素是一位数组。
System.out.println(Arrays.asList(a3));
//字符数组则按预想结果处理
System.out.println(Arrays.asList(a4));
打印结果:
java.lang.Object
java.lang.Object
[I@1a06f956
[[I@3fdb8a73
[Ljava.lang.String;@665ea4c5
[[I@1a06f956]
[[I@45edcd24, [I@7f371a59, [I@7aa30a4e]
[null, null, null, null]
基本类型的一维数组可以被当作Object类型使用,不能当作Object[]类型使用;非基本类型的一维数组,即可以当作Object类型使用,不可以当作数组型使用。
利用反射打印数组示例:
public static void printObject(Object obj)
{
Class cls = obj.getClass();
if(cls.isArray())
{
int len = Array.getLength(obj);
for(int i = 0; i< len; ++i)
{
printObject(Array.get(obj,i));
}
}
else
{
System.out.println(obj);
}
}
Arrays.aslist()方法处理int[]和String[]的差异:
Array工具类用于完成对数组的反射操作。
HashSet类:就是采用哈希算法存取对象集合。
HashSet中不能包含相同对象的两个引用,但是可能包含包含两个哈希值相同的对象。
总结:HashSet在存储对象引用时,先计算hashCode(可以在复写的hashCode方法中输出来看看是不是),然后在内存中看看是否已经存在引用。如果不存在直接存入集合,否则比较这些hash值相同的对象,如果新对象和已有对象都不equals,才存入新对象。
反射的作用:实现框架功能
框架调用自己编写的类。
框架不知道将会调用什么样的类,不能够new对象。
下面的方法通过读取配置文件className来决定运行时使用什么样的集合类型。
className文件中内容是:
className=java.util.ArrayList
public class RefelctTest2 {
public static void main(String[] args) throws Exception
{
InputStream is = newFileInputStream("className");
Properties perp = newProperties();
perp.load(is);
is.close();
String className =perp.getProperty("className");
//Collection coll = newArrayList();
Collection coll =(Collection)Class.forName(className).newInstance();
ReflectPoint rpt1 = newReflectPoint(3,4);
ReflectPoint rpt2 = newReflectPoint(3,4);
ReflectPoint rpt3 = newReflectPoint(9,9);
coll.add(rpt1);
coll.add(rpt2);
coll.add(rpt3);
coll.add(rpt3);
//根据配置文件中内容不同而不同:ArrayList是4,HashSet是2
System.out.println(coll.size());
}
}