JAVA基础
基本类型
-
enum 枚举
Size { SMALL , MEDIUM , LARGE , EXTRA }
Size s = Size . MEDIUM
每个枚举类型都有一个静态的 values 方法 , 它将返回一个包含全部枚举值的数组。
ordinal 方法返冋 enum 声明中枚举常量的位置 , 位置从 0 开始计数。
-
String
- String是对象 不是基本类型,可将变量重新赋值为新的字符串,但不能改变原始String内容
- 判断String是否相等用equals,==比较的将是俩个对象
-
常量
- 使用final声明常量为对象常量,每个对象都有。final CTIME = System.currentTimeMillis();
- 使用final static声明的类静态变量(类常量) static final CTIME = System.currentTimeMillis();
-
基本类型包装类
基本数据类型 包装类 byte java.lang.Byte boolean java.lang.Boolean short java.lang.Short char java.lang.Character int java.lang.Integer long java.lang.Long float java.lang.Float double java.lang.Double 转换方法
Integer i = Integer.valueOf(1) ; i.intValue(); Integer i = 123; //由于有自动装包拆包机制可直接声明
关于自动装箱还有几点需要说明 。 首先 , 由于包装器类引用可以为 null , 所以自动装箱有可能会抛出一个 NullPointerException 异常。
另外,如果在一个条件表达式中混合使用 Integer 和 Double 类型 , Integer 值就会拆箱 ,提升为 double , 再装箱为 Double。
对象与类
-
重载
类中允许同名函数不同参数的方法存在,根据调用时传递的参数类型自动执行对应的方法。
-
成员变量初始化
可直接赋值为常量值,也可为某个对象,也可为方法的返回值。
可通过初始化块统一初始化,初始化块将早于构造函数前运行
-
static
- 属于类,而不属于任何独立的对象。
- static变量直接赋值 public static int i = 1;
- 放到static块中同一初始化
- 可通过对象句柄调用静态方法,但是不建议。
static{ i = 0x123; j = "never"; }
-
构造函数
- 所有成员变量无论生命在类中任意位置,都将在构造函数执行前得到默认初始化。
- 构造函数名称与类名称一致。
- 很多类会重载一个无参数的构造函数用来初始化适当的默认值。
- 如果类中没有编写构造函数,系统会提供一个无参数的构造函数,如果有一个有参数的构造函数,但创建对象时未传递参数将发生错误。
- 可通过this(param)调用其他重载的构造函数。
-
初始化顺序
- 所有数据域被初始化为默认值 ( 0 、 false 或 null ) 。
- 按照在类声明中出现的次序 , 依次执行所有域初始化语句和初始化块 。
- 如果构造器第一行调用了第二个构造器,则执行第二个构造器主体
- 执行这个构造器的主体
-
final
- 当一个成员变量被声明为final时,系统将不会给与默认初始化,需要手动初始化,初始化后不能修改。对于其保存的是一个对象时,不可修改代表不能将变量替换为其他对象的句柄,但可以该对象的属性进行操作。
- 当一个类被声明final时,将不允许继承,其类的所有方法自动变为final,但不包含成员变量。
- 当一个方法被声明final时,将不允许重写。
-
方法参数
-
java总是按值传递,对于基本类型的参数发生变化后对原始变量毫无影响,对于对象参数传递的是对象的句柄(概念上不等于引用传递,因为对象变量的值就是句柄地址!),对对象进行的操作将影响外部变量。
此时变量a和b不会发生任何变化,因为a和b在方法内部被初始化为局部变量,对局部变量的修改不会影响到外部变量,但是对象的句柄地址与外部变量的地址一致,修改对象属性会生效。
总结:
一个方法不能修改一个基本数据类型的参数(即数值型或布尔型)。
一个方法可以改变一个对象参数的状态 。
一个方法不能让对象参数引用一个新的对象。
public class a{ public void swap(object a,object b){ object temp = a; a = b; b = temp; } }
-
-
finalize
- 可以为任何一个类添加 finalize 方法。
- finalize 方法将在垃圾回收器清除对象之前调用 。
- 在实际应用中,不要依赖于使用finalize 方法回收任何短缺的资源 , 这是因为很难知道这个方法什么时候才能够调用。
-
包
- 一个类可以使用所属包中的所有类,其他包中的所有public类
- 可以使用完整的包名+类名访问,也可通过import先导入包
- 当import的俩个包拥有同一个类名时需要通过完成包名+类名方式访问。
- 静态导入 import static java.lang.System . * ,import static java.lang.System.out ;执行system下的静态方法时无需加前缀。
- 使用package com.xxx.a 声明当前类所属包,否则将处于默认包中(default package),将类放到与包名相同的路径下,com/xxx/a。
- 编译器在编译源文件的时候不检查目录结构 。 例如 , 假定有一个源文件开头有下列语句 :package con . myconpany ;即使这个源文件没有在子目录 com / mycompany 下 , 也可以进行编译 。 如果它不依赖于其他包 , 就不会出现编译错误 。 但是 , 最终的程序将无法运行 , 除非先将所有类文件移到正确的位置上 。 如果包与目录不匹配 , 虚拟机就找不到类。
- 前面已经接触过访问修饰符 public 和 private。 标记为 public的部分可以被任意的类使用 ; 标记为 private 的部分只能被定义它们的类使用。 如果没有指定 public或 private , 这 个 部分 ( 类 、 方法或变量 ) 可以被同一个包中的所有方法访问。
-
类路径
- 将JAR文件放在一个目录中,例如 : /home/user/archives。
- 设置类路径(classpath)。 类路径是所有包含类文件的路径的集合 。/home/user/classdir : . : / home /user/archives/*,其中" : . :"中的“.”为当前路径。
- javac编译器总是在当前的目录中查找文件,但Java虚拟机仅在类路径中有目录的时候才查看当前目录如果没有设置类路径,那也并不会产生什么问题,默认的类路径包含目录.然而如果 设置了类路径却忘记了包含目录,则程序仍然可以通过编译,但不能运行。
- java - classpath /home/user/classdir : . : / home/user/archives/* app 在运行时设置class path
继承
-
基本概念
extends, super.xxx调用父类方法,super(param)调用父类构造函数。
如果子类的构造器没有显式地调用超类的构造器,则将自动地调用超类默认( 没有参数 )的构造器。如果超类没有不带参数的构造器 ,并且在子类的构造器中又没有显式地调用超类的其他构造器,则Java编译器将报告错误。
判断是否应该设计为继承关系,is-a规则,表明子类的每个对象也是父类的对象,如经理也属于员工。is-a另一种规则是置换法则,表示出现超类的任意地方都可以用子类对象替换。可以将子类的对象赋给超类的变量。
在 Java 中子类数组的引用可以转换成超类数组的引用 ,而不需要采用强制类型。但不建议这么做,所有数组都要牢记创建它们的元素类型 , 并负责监督仅将类型兼容的引用存储到数组中。
java中方法的名字和参数列表作为方法的签名,但返回值不是,如果子类拥有父类同样签名的方法将会覆盖父类的方法,在覆盖方法时一定要保证返回值的兼容性。允许子类返回值为父类返回值的子类型。
private,static,final方法编译器可以明确知道调用的是哪个方法,这种调用称为静态绑定。否则为动态绑定。
程序运行动态绑定时虚拟机需要从子类到父类逐一查找适用的方法,开销很大,所以虚拟机会提前为每个类建立一个方法表,虚拟机查询方法表签名找到调用方法,但用super.xxx()调用父类方法时会查询父类的方法表。
如果方法很简短 、 被频繁调用且没有真正地被覆盖 , 那么即时编译器就会将这个方法进行内联处理。 内联调用 e.getName()将被替换为访问 e.name 域 。
在Java 中,每个对象变量都属于一个类型 。类型描述了这个变量所引用的以及能够引用的对象类型。所以可以得到可引用的所有类。子类可以转为父类的引用,父类不能转为子类的引用。使用instanceof操作符可以检测是否可以被引用转换。
-
多态
- 存在继承关系
- 子类要重写(非重载)父类的方法
- 父类数据类型的引用指向子类对象。
- 那么我们可以根据以上情况总结出多态成员访问的特点:
成员变量
编译看左边(父类),运行看左边(父类)
成员方法
编译看左边(父类),运行看右边(子类)。动态绑定
静态方法
编译看左边(父类),运行看左边(父类)。
(静态和类相关,算不上重写,所以,访问还是左边的)
只有非静态的成员方法,编译看左边,运行看右边 - 多态后不能使用子类特有的属性和方法 。
-
抽象类
- 包含抽象方法的类也必须要声明为抽象。
- 抽象类可以包含成员变量和已经实现的方法。
- 抽象类不能实例化,但可以创建的抽象类的变量,但是只能引用已经实现全部方法的子类。
-
object
- Object.equals(obj1,obj2) 比较两个对象是否引用同一地址。
- 如果重新定义 equals 方法, 就必须重新定义hashCode 方法, 以便用户可以将对象插人到散列表中,Equals 与 hashCode 的定义必须一致 : 如果 x.equals (y ) 返回 true , 那么x.hashCode()就必须与 y.hashCode()具有相同的值。
- 整型数组类型 int[]可以被转换成Object, 但不能转换成对象数组。
-
arrayList
- arrayList管理着对象引用的一个内部数组。 最终 ,数组的全部空间有可能被用尽 。 这就显现出数组列表操作魅力 : 如果调用 add 且内部数组已经满了, 数组列表就将自动地创建一个更大的数组, 并将所有的对象从较小的数组中拷贝到较大的数组中 。如果已经清楚或能够估计出数组可能存储的元素数量 , 就可以在填充数组之前调用ensureCapacity 方法 :staff.ensureCapacity(100) ;这个方法调用将分配一个包含 100 个对象的内部数组。 然后调用100 次 add , 而不用重新分配空间。
- 容量为100的arrayList初始化完成后可能不包含任何元素,而size为100的arrayList则包含100个元素。
- staff.size()将返回 staff 数组列表的当前元素数量 , 它等价于数组 a 的 a.length 。
- 当确定arrayList大小后应调用trimToSize方法,多余的空间将会被垃圾回收。·
int actualSize = ...;//动态确定数组大小,但无法解决动态更改数组 Employee[] staff = new Employee[actualSize]; ArrayList<Integer> ids = new ArrayList<>(); //创建arrayList
-
可变参数
public void test(int... args){}
允许将一个数组传递给可变参数方法的最后一个参数:
System.out.printf ( "%d %s” new Object[] {new Integer(123) ,"widgets"} ) ;
等同于
System.out.printf("%d %s",123,"widgets");
-
反射
- 在运行时分析类的能力 。
- 在运行时查看对象 ,例如 , 编写一个 toString 方法供所有类使用。
- 实现通用的数组操作代码 。
- 利用Method 对象 , 这个对象很像中的函数指针。
- 在 java . lang . reflect 包中有三个类 Field 、 Method 和 Constructor 分别用于描述类的域 、 方法和构造器 。 这三个类都有一个叫做 getName 的方法 , 用来返回项目的名称。 Field 类有一个 getType 方法, 用来返回描述域所属类型的 Class 对象。 Method和 Constructor 类有能够报告参数类型的方法, Method 类还有一个可以报告返回类型的方法 。 这三个类还有一个叫做 getModifiers 的方法 , 它将返回一个整型数值, 用不同的位开关描述 public和 static 这样的修饰符使用状况。 另外 , 还可以利用 java.lang.refleCt 包中的Modifiei类的静态方法分析getModifiers返回的整型数值。例如,可以使用 Modifier 类中的 isPublic 、 isPrivate 或 isFinal判断方法或构造器是否是 public 、 private 或 final 。 我们需要做的全部工作就是调用 Modifier类的相应方法, 并对返回的整型数值进行分析 , 另外 ,还可以利用 Modifier . toString 方法将修饰符打印出来。
-
class类
在Java中,每个class都有一个相应的Class对象。也就是说,当我们编写一个类,编译完成后,在生成的.class文件中,就会产生一个Class对象,用于表示这个类的类型信息。
在运行期间,如果我们要产生某个类的对象,JVM会检查该类型的Class对象是否已被加载。如果没有被加载,JVM会根据类的名称找到.class文件并加载它。一旦某个类型的Class对象已被加载到内存,就可以用它来产生该类型的所有对象
obj.getClass()方法将会返回一个 Class 类型的实例 。
运用.class的方式来获取Class实例对于基本数据类型的封装类还可以采用.TYPE来获取相对应的基本数据类型的Class实例.
还可以调用静态方法 forName 获得类名对应的 Class 对象。
虚拟机为每个类型管理一个 Class 对象 。 因此 , 可以利用 = =运算符实现两个类对象比较的操作 。
newInstance方法可以创建一个相同类类型的实例,调用默认没有参数的构造函数,返回新创建的对象。
Math.class.getMethod ("sqrt", double.class); 获取sqrt方法
String className = "java.util.Random"; Class cl = Class.forName(className);//获得class对象 if (e.getClass() == Employee.class) //比较class对象 String s = "java.util.Random" ; Object m = Class.forName(s).newlnstance(); //创建相同类型实例
-
设计技巧
- 将公共操作和域放在超类
- 不要使用受保护的域,由于可能被更多的子类继承从而访问,破坏封装性。
- 使用继承实现 “ is - a ” 关系。
- 除非所有继承的方法都有意义,否则不要使用继承。
- 在覆盖方法时, 不要改变预期的行为。
- 使用多态 , 而非类型信息。
- 不要过多地使用反射
接口
- 基本概念
- 接口中的所有方法自动地属于 public。因此,在接口中声明方法时,不必提供关键字public.但实现接口的方法必须标为public。
- 在接口中还可以定义常量。double I = 1; 等于 public static final I = 1;
- 提供实例域和方法实现的任务应该由实现接口的那个类来完成。 因此 , 可以将接口看成是没有实例域的抽象类。
- 接口不能实例化,但是可以声明接口变量,其值必须为实现了接口的对象引用。
- 可以用instanceof检查对象是否实现了某个接口。
- 接口也可以被继承。
内部类
-
基本内部类
- 内部类方法可以访问该类定义所在的作用域中的数据,包括私有的数据。
- 内部类可以对同一个包中的其他类隐藏起来。
- 当仅仅想定义一个回调函数时不想创建一个文件类时,使用匿名内部类比较便捷。
- 内部类的对象总有一个隐式引用,指向了外部类对象。这个引用在内部类的定义中不可见。
- 内部类的隐式引用对象在内部类中构造函数中创建。
- 只有内部类可以声明为私有的,常规类只可以具有包可见性,或公有可见性。
- 在外部类作用域之外,可以这样访问内部类.OuterClass.InnerClass.
- 内部类中的静态成员变量必须为final,对于每个外部对象会分别有有一个内部类实例,如果这个成员变量不是final,它肯能就不是唯一的。
- 内部类不能有static 方法。内部类是一种编译器现象,与虚拟机无关,编译器会把内部类翻译成$分隔外部类名与内部类名的常规类文件,而虚拟机对此一无所知. 如: outerClass$InnerClass
- 内部类可以访问外部类的私有成员变量,是通过在外部类添加静态方法,静态方法将外部类对象作为参数,返回私有成员的值。
-
局部内部类
- 局部类不能用public或private访问说明符声明,它的作用域被限定在这个局部块中(一个方法中).
- 局部可以对外部世界完全隐藏起来,其他外部类的其他方法也不能访问。除了在声明的局部方法中,没有任何方法知道局部类的存在。
- 局部类相比其他内部类,不仅能够访问外部类,还可以访问定义局部类方法内的局部变量,不过那些局部变量必须事实上为final,这说明,它们一旦赋值就绝不会改变。局部内部类将方法中的局部变量在局部类中创建成员变量进行备份(这也说明了为什么必须事实上为final),防止包含局部类的方法执行完毕后将局部变量销毁。
-
匿名局部类
- 假如只创建这个类的一个对象,就不必命名了,这种类被称为匿名内部类。
- 语法格式:new SuperType(params){ 匿名局部类成员变量和方法 },其中SuperType可以是接口,于是内部类就要实现这个接口,也可以是一个类,于是内部类就要拓展它。
- 由于构造函数的名字必须与类名相同,匿名类没有类名,所以匿名类不能有构造函数。
-
静态内部类
- 当使用内部类只是为了把一个类隐藏在另外一个类的内部,并不需要引用外围类对象,此时可以将内部类声明为static,以便取消产生的引用。
- 静态内部类可以有静态成员变量和方法。
- 只有内部类可以声明为static,静态内部类的对象除了没有对生成它的外围类对象的引用特权外,与其他的所有内部类完全一样。如果在内部类中声明了静态方法确没有将类声明为static,编译器将会给出警告,没有可用的隐式外围类型对象初始化内部类对象。