ThinkChat2.0新版上线,更智能更精彩,支持会话、画图、阅读、搜索等,送10W Token,即刻开启你的AI之旅 广告
[TOC] 面向对象(OOP)的程序是由对象组成的,每个对象包含对用户公开的特定功能部分和隐藏的实现部分。 # 类 ## 面向对象三大特性 封装、继承、多态 * 封装隐藏了类的内部实现机制,可以在不影响使用的情况下改变类的内部结构,同时也保护了数据 * 继承可以重用父类代码,实现代码复用和可扩展的效果 * 多态指对象的一个方法在调用时可以有多种状态,即父类引用可以持有子类对象 * 多态实现的两种形式:基于继承重写、基于接口 * 多态存在的三个条件:继承、重写、父类引用持有子类对象 * 非静态成员方法的调用:编译看父类,运行看子类 * 多态优点:不用创建一堆子类对象的引用;多态缺点:不能使用子类特有的成员属性和成员方法 ***注意点*** 1、实现封装的关键在于,绝对不能让类中的方法直接地访问其他类的实例域(对象中的数据),程序仅通过对象的方法与对象数据进行交互。 2、对象状态的改变必须通过调用方法实现。 ## 类之间的关系 依赖、聚合、继承 * 依赖是最明显、最常见的关系,如果一个类的方法操纵另一个类的对象,就说一个类依赖于另一个类 * 聚合指类A的对象包含类B的对象 ## 自定义类 1、在一个Java源文件中,只能有一个公有类,但可以有任意数目的非公有类。 2、显式参数指方法名后面括号中的数值,隐式参数指出现在方法名前的对象(在方法内部可通过this来表示隐式参数)。 3、静态方法是一种不能向对象实施操作的方法,也就是没有隐式参数,不能操作对象,但可以访问自身类中的静态域。 4、按值调用表示方法接收的是调用者提供的值,按引用调用表示方法接收的是调用者提供的变量地址。 5、类设计技巧: * 一定要保证数据私有 * 一定要对数据初始化,可以提供默认值,也可以在构造器中设置默认值 * 不要在类中使用过多的基本类型 * 不是所有的域都需要独立的域访问器和域更改器 * 将职责过多的类进行分解 * 类名和方法名要能够体现它们的职责 ***重点*** * Java总是采用按值调用,方法得到的是参数值的一个拷贝,不能修改传递给它的任何参数变量的内容 ## 内部类 将一个类的定义放在另一个类的定义内部,这就是内部类 内部类分为成员内部类、静态内部类、局部内部类和匿名内部类 ### 成员内部类 相当于外部类的一个成员。 ```java public class OuterClass { private String mString = "a"; private void outerMethod() { // 使用内部类属性或方法需要提供内部类实例来实现 InnerClass innerClass = new InnerClass(); innerClass.innerMethod(); } public class InnerClass { // private static String mStaticString; ERROR! public void innerMethod() { // 使用外部类属性 mString = "b"; // 使用外部类方法方法 outerMethod(); } } public static void main(String[] args) { OuterClass outerClass = new OuterClass(); OuterClass.InnerClass innerClass = outerClass.getInnerClass(); innerClass.innerMethod(); } } ``` 1、访问规则 * 内部类可以访问外部类的所有属性和方法,即使是私有的 * 外部类要访问成员内部类的属性和方法,则需要通过成员内部类的实例来实现 2、注意事项 * 成员内部类中不能有任何 static 的属性和方法 * 成员内部类是依附于外部类的,只有先创建了外部类对象,才能够创建成员内部类,推荐使用 getxxx() 来获取成员内部类 * 成员内部类中持有一个外部类的引用 ### 静态内部类 静态内部类是一个碰巧被声明在外部类中的普通类。 ```java public class OuterClass { private static String sString; private void outerMethod() { // 外部类可访问私有静态类的属性和方法 InnerClass innerClass = new InnerClass(); innerClass.innerMethod(); } private static class InnerClass { private void innerMethod() { // 静态内部类只可以访问外部类的静态成员 sString = "a"; } } public static void main(String[] args) { // 创建静态内部类对象不需要外部类对象 InnerClass innerClass = new InnerClass(); innerClass.innerMethod(); } } ``` * 静态内部类不持有外部类的引用 * 和其他静态成员一样,被声明为 private 时,只有外部类可访问 * 静态内部类不能直接访问外部类的任何非 static 成员变量和方法;却可以直接访问外部类的静态成员,包括私有的 * 创建静态内部类时不需要创建外部类对象 * 声明在接口中的内部类自动成为 public 和 static ### 局部内部类 ```java void setData() { class ClickListener implements View.OnClickListener { @Override public void onClick(View v) { // do click } } mView.setOnClickListener(new ClickListener()); } ``` 局部内部类嵌套在方法和作用域内,像是方法里的一个局部变量一样 * 局部内部类对外界完全隐藏,即使是外部类也不能访问它 * 局部内部类可以访问所在作用域的局部变量,但必须声明为 final * 有时候 final 的限制会不方便,可以使用一个长度为 1 的数组类替代 ### 匿名内部类 ```java void setData() { mView.setOnClickListener(new View.OnClickListener() { // do click }); } ``` 对局部内部类的一种深化,只需要创建局部内部类的一个对象时,就不需要命名了 1、定义 * 匿名内部类语法格式为 `new SuperType(construction parameters){}` * 父类后面括号中的参数会传递给父类的构造器 * 匿名内部类终归还是个类。如果 SuperType 是一个接口,匿名类实现这个接口,并扩展自 Object 类;如果 SuperType 是一个类,则匿名类是该类的子类 2、注意 * 匿名内部类属于局部内部类,局部内部类的限制对于它同样生效 * 匿名内部类中不能定义构造方法,因为它连名字都么有,但可以使用初始化语块代替构造方法 匿名内部类初始化语块示例: ```java invite(new ArrayList<String>(){ { add("Harry"); add("Tony"); } }; ); ``` ### final关键字的理解 1、final的意思是【无法改变的】、【最终的】,可以修饰非抽象类、非抽象类的成员变量和成员方法 2、final类不能被继承,没有子类,其中的方法默认是final的 3、final方法不能被重写,但可以被继承(final不能用于修饰构造方法) 4、final成员变量表示常量,只能被赋值一次,赋值后不再改变。 5、使用final声明基础数据类型时,数值恒定不变;使用final声明对象引用时,引用的对象恒定不变,但对象的数据可变;使用final声明数组类型时,引用的数组恒定不变,但数组内的数据可变。 优点: 1、编译器遇到final方法时会转入内嵌机制,提高效率。 2、可以安全的在多线程环境下共享,不需要额外的同步开销。 ### 为什么匿名内部类使用局部引用要用final 1、匿名内部类属于一种局部内部类。 2、编译后局部内部类中会有一个成员变量,是对外部局部变量的引用的拷贝,在局部内部类使用外部局部变量值时都是通过这个引用进行的。 3、为避免这个成员变量的值(引用的对象)被外部类的方法修改,导致内部类在使用时得到的值不一样,需要使用final让该成员变量不可变。 4、局部变量位于方法内部,在虚拟机的栈上,意味着这个变量无法进行共享,匿名内部类无法直接访问,只能通过值传递的方式,传递给匿名内部类。 5、而类的成员变量在虚拟机的堆上,内部类可以直接获取这个变量,故类的成员变量不需要声明为final内部类就可访问。 参考链接 [java提高篇(八)----详解内部类](https://www.cnblogs.com/chenssy/p/3388487.html) # 对象 1、一个类的多个方法有相同的名字、不同的参数,便产生了重载。编译器通过用各个方法给出的参数类型与特定方法调用所使用的值类型进行匹配来挑选出相应的方法,这个过程称为重载解析。 2、完整的描述一个方法,需要指出方法名以及参数类型,这叫做方法的签名。返回类型不是方法签名的一部分,因此不能有两个名字相同、参数类型也相同却返回类型不同的方法。 # 继承 ## 继承的 private 成员问题 关于子类是否可以继承父类 private 成员问题,原来大家已经讨论很久了: 一种是子类不可以继承父类的私有成员,私有成员是默认为 final 的,是不允许覆盖的。但父类提供了 public、protected 方法访问其私有成员时,子类或外部类可以通过 public、protected 方法访问到。 另一种说法是子类继承父类的所有成员,包括私有的。private,public,protected 和继承没有关系,他们对成员函数和变量的限制只是在成员的可见性上。因此私有成员变量和方法在子类中不可见,也就是拥有它,但不能使用它,必须通过父类的 public、protected 方法访问。 个人比较倾向于第一种说法。可以看下参考中的[《private 继承之争》](http://www.ithome.com.tw/voice/90371)一文,讲解的比较不错。 参考 [private 继承之争](http://www.ithome.com.tw/voice/90371) [私有的成员能被子类继承吗?](http://www.blogjava.net/sitinspring/archive/2007/12/04/165288.html) [Java 中子类是否会继承父类中的 private 成员](http://woyixiaorenne.iteye.com/blog/2312477) ## 继承的构造方法问题 子类和父类的构造方法不存在继承关系,而是调用关系,存在以下原则: * 子类构造过程中必须调用父类的构造方法 * 如果子类没有显示调用父类构造方法,则系统默认调用父类的无参构造方法(若系统无无参构造方法,编译报错) * 一个类未声明任何构造方法时,系统会默认生成一个无参构造方法。当声明了有参构造方法时,就不会自动生成无参构造方法了 静态代码块、构造代码块、构造方法的执行顺序: 父类静态代码块→子类静态代码块→父类构造代码块→父类构造方法→子类构造代码块→子类构造方法 参考文档: [关于 Java 内部类的小抄](http://blog.jrwang.me/2016/java-inner-class/)