Java面向对象总结

PS.只是针对易混淆的一些细节的笔记,并不严谨

面向对象总结

this

形参个数可变方法 ...

方法 重载

成员变量与局部变量

包package、import、import static

package

import

构造器

重写

super

初始化块

Java8增强包装类

== 和 equals

单例模式

final


 

面向对象总结

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.被其他类实现

没总结完。。。待续。。。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值