3 面向对象概述
面向对象范式比面向过程范式有优势的地方。面向过程范式重在设计方法,数据和数据上的操作是分离的。而面向对象范式将数据和方法都组合在对象中。
面向对象范式结合面向过程的功能以及将数据和操作集成在对象中的特性。
不可变对象(immutableobject)是指随着对象的创建,内容不能在改变的对象。要成为一个不可变的类,必须满足下面的需求:
1、 所有数据域是私有的
2、 没有修改器
3、 没有访问器,它会返回一个指向可变数据域的引用。(有点不理解)
如果一个类中所有数据域都是私有的且都是基本类型,而且这个类也不包含set方法,该类是不可变的。
public class A {
private int[] values;
public int[] getValues() {
return values;
}
}
重载方法意味着可以定义多个同名的方法,但是有不同的签名。重写或者覆盖意味着为子类提供一种全新的实现。
什么是方法重载(overloading)?
1、必须在同一个类里。
2、方法名相同。
3、参数列表(方法参数的个数和参数类型)不同。
4、与方法返回值和方法修饰符没有任何关系。
什么是方法重写(覆盖)-(method overrideing)?
子类需要修改父类中定义方法的实现,这便称作重写或者覆盖
1、 父类中私有方法不能重写。
2、 访问权限不能更低。
3、 父类是静态方法,子类也必须通过静态方法重写。
避免命名冲突最好的方法是将这些类放到不同包(不同文件夹)中。
面向对象三个特点:封装,继承和多态。
4.1抽象和封装
封装的意思是隐藏对象的属性和实现细节,就对外提供公公访问方式。好处如下:
1、 提供公共访问方式;
2、 提高代码复用性
3、 提高安全性
4.2继承
从已有的类派生出新类,这称作继承(inheritance)。继承用extends 关键字。继承的目的是重用父类代码。
1、 子类并不是父类的子集,子类往往包含更多的信息和方法。
2、 父类的私有数据域在该类之外是不可以被访问到的,但是可以通过父类的公公访问的访问器和修改器来访问和修改他们。
3、 继承是为是 关系建模的,父类和子类存在是的关系。
4、 Java中不允许多重继承(multiple inheritance),只能单一继承(single inheritance)。多重继承可以用接口实现。
子类可以继承父类的数据域和方法,但是父类的构造方法不被子类继承的。也可以通过super 关键字调用父类的构造方法和方法。另外super语句必须出现在子类构造方法的第一行。
构造方法可以调用重载的构造方法或者父类的构造方法。如果父类构造方法他们都没有被显示的调用,编译器就会自动的将super()无参构造 作为构造方法的第一条语句,反正父类构造方法肯定会被调用(下面例子就是一个错误例子)。但是如果明确调用父类的有参构造,,无参是不会被调用的。有多层继承关系的就会形成构造方法链(constructor chaining)。由于调用的是无参构造,为了方便对该类进行扩展,最好创建一个无参构造类。
重写的实例方法需要用super.方法名才能调用父类隐藏的方法。
public class Test extends Test1{
public Test(int i) {
System.out.println(i);
}
public static void main(String[] args) {
Test test = new Test(5);
}
}
class Test1 {
public Test1(int j) {
System.out.println(j);
}
}
Object类中tostring() 会返回一个描述该对象的字符串,他由类名,@和该对象十六进制形式的内存地址组成的字符串。
另外,println(object)等价于println(object.tostring)。
final关键字:(Java 中关键字都是小写)
1. 防止类扩展,使用final关键字表明类是终极的,是不能作为父类的,Math就是一个终极类;
2. 也可以定义一个方式为终极的,一个终极方法是不能被覆盖的。
3. 只有final 修饰符可以用在方法中的局部变量。方法内的终极局部变量就是常量。
4.3多态
子类是它的父类的特殊化,每个子类的实例都是父类的实例。
使用父类对象的地方都可以使用子类的对象,这就是通常所说的多态(polymorphism)。简单来说,多态意味着父类型的变量可以引用子类型的对象。
一个方法可以在父类中定义而在它的子类中覆盖。在多态中,涉及到声明类型(declared type)和实际类型(actual type)。引用类型变量可以是一个null值或者是一个对声明类型实例的引用。实例可以使用声明类型或者子类型的构造方法创建。 变量的实际类型是被变量引用的实际类。
调用哪个重写的方法由实际类型决定,这称为动态绑定(dynamic binding)。
多态性就是相同的消息使得不同的类做出不同的响应。
多态存在的三个必要条件:
一、要有继承;
二、要有重写;
三、父类引用指向子类对象。
对象转换也是多态中非常重要的一块,分为隐式转换(implicit casting)和显示转换(explicit casting)。
1、隐式转换如下所示:
m(new student()) , 将对象newstudent() 赋值给一个object类型的参数。等价于:
Object o = new Student();
m(o);
2、显示转换
Student p = o;
这句话是错误的,即便o 实际就是一个Student()对象,但是编译器不能识别。显示转换类似强制转换,用圆括弧把目标对象的类型括住,放到要转换的对象前面。
Student b = (Student)o;
子类的实例总可以转换为父类的变量(向上转换upcasting), 因为子类的实例永远是父类的实例。当把父类的实例转换成子类变量(向下转换downcasting),必须使用“(子类名)“进行显示转换。
确保一个对象是另外一个对象的实例,可以使用instanceof来实现。经常拿来判断,如
if(变量 instanceof 类名) 。
下面两个例子就是看下大家有没有真正理解多态的概念:
Circle circle = new Cricle(5);
GeometricObject object = circle;
GeometricObject object = newGeometricObject();
Circle circle = (Circle)object;
哪个编译是正确的?第一个
总结:父类变量引用子类对象之后,可以使用父类的方法,重写的方法以及向下转换的子类的方法。
这个例子确实很经典,这个做对了,基本上你就理解我总结的那句话了。
class A { public Stringshow(D obj) { return ("A and D"); } public Stringshow(A obj) { return ("A and A"); } } class B extends A{ public Stringshow(B obj){ return ("B and B"); } public Stringshow(A obj){ return ("B and A"); } } class C extends B{ } class D extends B{ } public class Test { public static void main(String[] args) { A a1 = new A(); A a2 = new B(); B b = new B(); C c = new C(); D d = new D(); System.out.println("1---" + a1.show(b)); System.out.println("2---" + a1.show(c)); System.out.println("3---" + a1.show(d)); System.out.println("4---" + a2.show(b)); System.out.println("5---" + a2.show(c)); System.out.println("6---" + a2.show(d)); System.out.println("7---" + b.show(b)); System.out.println("8---" + b.show(c)); System.out.println("9---" + b.show(d)); } } 1---A and A 2---A and A 3---A and D 4---B and A 5---B and A 6---A and D 7---B and B 8---B and B 9---A and D