PS.只是针对易混淆的一些细节的笔记,并不严谨
面向对象总结
this
1.this用于构造器(代表当前正在初始化的对象)、对象实例方法(当前调用的对象)
2.this不能用于static修饰的类方法,因为类方法加载的时候对象实例还没有初始化
3.当在对象方法中调用一个被局部变量覆盖过的成员变量的时候用this可以找到该变量
形参个数可变方法 ...
定义形参格式:type... parameters
1.这个参数可以近似理解为数组占位,调用时候实参传入数组是没问题的,
2.可变参数必须定义在最后一位,而入参数组的位置就随便了
3.数组和可变方法,当类型相同的时候是不能重载的,数组方法和可变形参同时定义会编译报错
但是形参数组的类型为父子关系时候,优先调用数组方法
// 定义类
class Person{
public void changableTest(String str, Integer[] ints){
System.out.println("数组方法被调用");
}
public void changableTest(String str, Object... ints){
System.out.println("可变方法被调用 。。。");
}
}
// 定义测试方法
@Test
public void test4(){
Person person = new Person();
person.changableTest("测试开始", new Integer[]{1, 2, 3}); // 数组方法被调用
// 注意:int[]类型默认转换为Object[]而不是Integer[],事实上int[]数组不会装箱为Integer[]
person.changableTest("测试开始", new int[]{1, 2, 3}); // 可变方法被调用 。。。
person.changableTest("测试开始", 3,2,1); // 可变方法被调用 。。。
}
4.普通方法和可变形参,以下情况可以重载,优先调用普通方法。不推荐重载可变参数方法,会降低可读性
class Person{
public void overLoad2(String str){
System.out.println("单个String形参方法");
}
public void overLoad2(String... str){
System.out.println("可变形参String形参方法");
}
}
@Test
public void test5(){
person.overLoad2(); // 可变形参String形参方法
// 同时满足普通方法和可变形参
person.overLoad2("单个形参字符串"); // 单个String形参方法
person.overLoad2("多个形参字符串", "板凳"); // 可变形参String形参方法
person.overLoad2(new String[]{"数组"}); // 可变形参String形参方法
}
方法 重载
条件:
0.调用者相同(类,或对象实例)
类方法和实例方法相互重载,因为java可以用实例调用类方法,所以用对象实例调用某方法,java是分不清你想调用对象实例方法还是类方法的
1.方法名相同
2.形参列表不同
重载与访问修饰符、返回值类型没关系,当然用private修饰方法虽然重载了,但是在外面调用不到,则会将参数自动转化为父类(例如将int类型转换为Object类型),所以留心是否能访问到
// Person类的方法
class Person{
protected int overLoad1(Object obj){
System.out.println("protected重载数组方法被调用");
return 0;
}
public void overLoad1(String obj){
System.out.println("public重载数组方法被调用");
}
private void overLoad1(int i){
System.out.println("private重载数组方法被调用");
}
}
@Test
public void test5(){
Person person = new Person();
person.overLoad1("string lalala"); // public重载数组方法被调用
person.overLoad1(5); // protected重载数组方法被调用
}
自动转换类型,对于数组,提升到Object是可以的,但对于装箱 int[]不能自动提升为Integer[], 反之拆箱也是不成立的
@Test
public void test6(){
Person person = new Person();
person.overLoad3(new int[]{1,2,3}); // overLoad3方法---int
// 自动转换类型,对于数组,提升到Object是可以的,但int[]不能自动提升为Integer[], 反之拆箱也是不成立的
person.overLoad3(new String[]{"str"}); // overLoad3方法---Object
}
class Person{
public void overLoad3(Integer[] ints){
System.out.println("overLoad3方法---Integer");
}
public void overLoad3(int[] ints){
System.out.println("overLoad3方法---int");
}
public void overLoad3(Object[] ints){
System.out.println("overLoad3方法---Object");
}
}
成员变量与局部变量
成员变量(类、实例)无需显式初始化,系统在准备阶段会自动将其初始化,与数组元素初始化相同
类变量存储的内存区属于类内存,不属于某个实例的内存,但实例变量存储在实例内存区中
局部变量必须显示初始化,否则不可访问。成员变量在类(实例)初始化的时候,系统就为其分配了内存空间,而局部变量在声明的时候系统并没有为其分配内存,当给局部变量赋值的时候,系统才为局部变量分配内存空间,并将初始值保存到这个内存空间中
一个类中不能定义2个同名的成员变量,1类变量和1实例变量也不行,还是那个原因,java实例调用分不清
允许成员变量和方法中的局部变量同名,此时局部覆盖成员,想调用成员变量,可用this.实例变量或类.类变量
包package、import、import static
package
包和子包并没有继承的概念,当在一个包中使用一个子包中的类的时候,也要引入全类名,或者import这个子包,包和自己的子包也是不同的包的关系
package必须作为源文件中第一条非注释语句
引入包也要写全类名,不能用相对包路径代替
import
导入包时候所使用的*不能代表包,只能代表类,所以星号对子包是没效果的,必须包名.星号
jdk1.5以后,可以只导入某一个类的静态方法、变量,使用的时候都不用写类名
import static package.subpackage.Classname.fieldName或者methodName或者*;
import static java.lang.System.*;
public void StaticImportTest{
public static void main(String[] args){
out.println("没有用类名");
}
}
构造器
如果自定义了构造器,则系统就不会提供默认的无参构造器了,如果需要使用,需显示定义
使用this调用重载构造器的时候,只能在构造器中使用,this所调用构造器的语句必须作为构造器的第一句执行。如果一个构造器里面调用了两个重造构造器,系统会提示Call to 'this()' must be first statement in constructor body,这显然违反了first statement的规定
class Person{
public int n = 3;
public int m = 4;
public int o = 9;
public Person(){} // 显示定义无参构造器
public Person(int n){
this.n = 3;
}
public Person(int m, int o){
this();
// this(3); // 此行放开报错报错 super()同理
this.m = 4;
this.o = 9;
}
}
重写
重写规则:两同两小一大
两同:方法名相同,形参列表相同
两小:子类方法返回值类型,子类方法抛出的异常应该比父类更小或相等
一大:子类方法的访问权限比父类的访问权限更大或相等
父子类方法要同为类方法或者同为实例方法,不能一类一实例
被覆盖后,子类无法访问父类被覆盖方法,但可用super(实例方法)或者类名(类方法)调用
super
当调用一个成员变量的时候,没有显式指定程序的调用者,则程序按照以下顺序寻找变量:
1.该方法中是否定义了该局部变量
2.当前类中是否定义了该成员变量
3.直接父类是否定义了该成员变量,直到找到Object都没有,程序就会报编译错误
外部调用子类变量:成员变量也能被继承,无论类,实例变量的同名定义,都可以改变类型(类型被更新),改变访问修饰符(子类访问修饰符会更新变量访问状态),甚至类和实例互相改变(子类static或者非static会更新属性状态),所以其本质上并不是重写,只是由于以上3条做到的隐藏。基本可以理解为子类对父类是降维打击式的隐藏,只要同名,就会影响父类属性
内部调用父类变量就可以super(实例变量时候)了,当然父类的private是调用不到的
对于构造方法中用super()调用父类构造器,和this()一样必须写在此构造方法的第一行,所以二者不能同时出现。但是事实上子类总会调用父类super构造器,分为以下3中情况:
1.子类构造显式调用父类的构造器
2.子类调用this本类重载构造器,追踪这个方法,这个被调用的构造器追到最原始的构造方法,会调用父类构造器(不一定显式调用)
3.一个构造方法既没有显式调用this也没super,系统还是会在执行子类构造器之前,隐式调用父类空参构造器。建议重写父类构造器后最好再显式定义一个父类空参构造器,因为子类可能会调用之,此时父类构造器重写过,系统就不给免费空参构造器使用了,需要我们自己再手动定义一个父类空参构造器,以此类推创建任何对象总是最先执行Object类的构造器
多态
实例方法 编译看左边,运行看右边
其实上面这句话理解为,针对实例方法,能不能运行这个方法看左边,运行什么具体内容看右边
结论是子类变量赋值给父类,除了实例方法指向子类重写的方法,其余成员变量和类方法都是指向父类内存 向上转型后执行的还是子类实例方法,而成员变量不具有多态性,多态特指实例方法
@Test
public void test7(){
Cat cat = (Cat)new CatSon();
System.out.println(cat.a); // 1
System.out.println(cat.b); // 3
cat.functionA(); // zi类实例方法
cat.functionC(); // 父类 类方法
Cat.functionC(); // 父类 类方法
CatSon.functionC(); // zi类 类方法
}
class Cat{
public int a = 1;
public static int b = 3;
public void functionA(){
System.out.println("父类实例方法");
}
public static void functionC(){
System.out.println("父类 类方法");
}
}
class CatSon extends Cat{
private String a = "子类a";
public static String b = "子类b";
public void functionA(){
System.out.println("zi类实例方法");
}
public static void functionC(){
System.out.println("zi类 类方法");
}
}
初始化块
修饰符只能是static,或者不写代表非静态
初始化块在执行构造器之前执行
普通初始化块和声明变量且显示赋值语句,二者谁在前谁先执行 无论定义实例变量语句是否显示赋值,初始化的时候都是先分配内存,给实例变量分配初始值(0,0.0,true,null),再进行赋值,虽然是一句代码,但分配内存初始化和赋值两个动作是分开进行的
类加载+创建实例变量的执行顺序: 1.父类静态代码块 2.子类静态代码块 3.父类非静态代码块 4.父类构造器 5.子类非静态代码块 6.子类构造器
Java8增强包装类
包装类和String类型转换方法
@Test
public void test8(){
// String转数字
Integer i = new Integer("8");
System.out.println(1 + i); // 9
Integer i2 = Integer.parseInt("88");
System.out.println(1 + i2); // 89
// 数字转String
String str = String.valueOf(88);
System.out.println(str + 1); // 881
}
// 数字类型包装类可以直接与数字比较大小,但包装类和包装类实例属于类的比较,缓存情况特殊-128~127相等
@Test
public void test9(){
System.out.println("比较大小:" + (1 == new Integer(2))); // 比较大小:false
System.out.println("比较大小:" + (new Integer(2) == new Integer(2))); // 比较大小:false
Integer i1 = 2;
Integer i2 = 2;
System.out.println("比较大小:" + (i1 == i2)); // 比较大小:true 有点像String,缓存中-128~127相等
System.out.println("比较大小:" + (new Integer(128) == new Integer(128))); // 比较大小:false
Integer i3 = 128;
Integer i4 = 128;
System.out.println("比较大小:" + (i3 == i4)); // 比较大小:false
}
== 和 equals
==判断
1.两个量是基本数据类型,没必要类型完全一样,结果是判断二者值是否相等 2.两个量是引用数据类型,判断是否指向同一个对象,两个量有父子关系参可以比较,否则会报错
String 直接量 的 == 判断如下,编译时候能确定的字符串用直接量代替,不能确定的(有引用的)作为new String()处理
@Test
public void test10(){
// 测试字符串 ==
String str1 = "我";
String str2 = "爱";
String str3 = "北京";
String str4 = "我爱北京";
String str5 = "我" + "爱" + "北京";
String str6 = str1 + "爱" + "北京";
System.out.println(str4 == str5); // true
System.out.println(str4 == str6); // false
}
单例模式
私有构造器,私有静态变量存放实例对象,公共静态方法获取实例
final
1.final修饰的类成员变量在类初始化的时候分配内存,可以在定义时候赋值,也可以在静态代码块中赋值,二者选其一。final修饰的实例变量同理,在对象初始化的时候分配内存,可以在定义时候赋值,也可以在非静态代码块中或者构造方法中赋值,三者选其一。不赋值系统不会为其隐式分配默认值(报错未在构造其中初始化) 不要再未显示初始化final变量之前访问该变量,会报错
2.final定义的局部变量,系统不会给局部变量默认赋值,所以需要显式初始化。定义时候赋值,或者定义时候不赋值而是在后面赋值一次
3.final指向引用变量,引用不能重新赋值,但是该引用例如数组的元素和对象的成员变量可以任意改变
4.宏替换:当final修饰的变量,赋初始值后,在编译时候就能直接确定的变量,在编译的时候该变量就会被直接替换成这个初始值。如果基本算数运算,字符串连接,没有使用到方法,普通变量调用,则编译器也会当作宏替换处理。对于宏替换效果的final“变量”只能在定义时候赋值才能有效果
5.final修饰的方法不能被子类重写(重写报错),但可以方法重载。对于private修饰的final方法,子类访问不到,所以即使子类有同名同参同返回值也不能称为重写,当然不会报错
6.final修饰的类不可以被继承
abstract
1.abstract加在类或者方法上,不能加在成员变量上,不能加在代码块和构造器上
2.抽象类可以没有抽象方法,但有抽象方法则类一定要抽象(接口)
3.抽象类可以有所有成员,但不能实例化,其初始化代码块和构造器是给实现类(子类)调用的
4.final和abstract不会同时使用,前者不能被继承,重写;后者只能被继承,实现
5.static和abstract不能同时修饰方法,即没有静态抽象方法;但是二者可以同时修饰内部类,即静态内部类
6.private和abstract不能同时修饰方法,因为abstract的方法被实现类实现才有意义(被子类重写)才有意义,所以private和abstract的方法是用不会被实现的,是没有意义的
interface
更抽象的抽象类
1.interface接口内部可以有抽象方法签名和默认方法,类方法,静态常量,内部类
2.修饰符和类一样,可以public或者默认包
3.一个接口可以有多个直接父接口,但是不能继承类
4.接口的本意是一种规范,内部不能包含初始化代码块和构造方法
5.可以包含的成员变量只能是静态常量,可以有内部类,内部接口,每部枚举类
6.内部成员只能是public修饰,即使省略访问修饰符也代表public
7.对于接口的静态变量而言,系统默认增加public static final,语法上即使不写也是这个意思。因为接口没有初始化代码块和构造方法,所以成员变量必须在定义时候给初始化值
int MAX_SIZE = 50;
public static final int MAX_SIZE = 50;
以上两种写法效果相同
8.接口里面的普通方法默认为public abstract的,语法上即使不写也是这个效果,不能有方法体。静态方法和默认方法可以有方法体。
9.接口里面内部类,内部接口,内部枚举类都是public static的,语法上即使不写也是这个效果
10.默认方法需要用default修饰,default和static不能同时修饰一个方法
接口的继承和实现
接口支持多继承,父接口写在extends后,用逗号隔开,子接口继承父接口的常量 接口支持多实现,使用implements,接口名字写在后面,逗号隔开 接口的实现和类继承类似,可以获得接口的常量,抽象方法,默认方法等
接口的作用有三个:1.定义引用 2.使用接口常量 3.被其他类实现
没总结完。。。待续。。。