1.面向编程:我们再解决问题中,注重的是解决问题的每一个步骤和过程。
以计算机的角度去看待问题
2.面向对象:注重的是在为题中,设计的到哪些对象,以及对象之间有哪些关系
以自己的角度去看待问题
为什么使用面向对象?/面向对象的好处:一点点东西的变化不会影响到整个程序,把错误的控制在一个局部的范围
|
核心思想 |
缺点 |
算法 |
面向编程 |
自项向下,逐步求精,也就是把功能分解 设计数据结构
编写基础代码,基于数据结构 |
一旦数据结构发生修改,可能必须就要修改操作的代码。
可重用性差
可维护性差,维护的成本高 |
程序=数据结构+算法 |
|
|
相对的优点 |
|
面向对象 |
分解数据
数据和操作数据是一个整体 |
数据修改,只涉及这对该数据的操作(封装) 可复用性强(继承)
可维护性高,维护的成本低 |
程序=对象+消息 |
万事万物皆对象,对象是由静态的属性和动态的方法来组成的类,是一组具有相同属性和行为的对象的抽象。
消息:对象之间的通信和响应。
三大特征:封装,继承,多态
类的组成:成员变量和成员方法
定义: 存在一个边界,边界之内的细节隐藏起来,只留下对外的接口(如,笔记本,手机,饮水机)
为甚么使用封装?:(1)易用,简单(2)安全(3)易维护
封装的好处:
1.只能通过规定方法访问数据
2.隐藏类的实现细节
3.方便加入控制语句
4.方便修改实现经过封装的属性,不能直接访问,要通过公共属性get/set方法访问。
如何实现继承:
在java语言中,用extends(扩展)关键字来表示一个类继承了另一个类。
允许后代直接使用先辈的所有属性和行为
为甚麽使用继承:更好的实现代码的重用
定义:相同的消息发送到不同的对象时,产生的响应不同
多态的体现:重载、重写等
多态的前提
1. 必须是类与类之间有关系。要么继承,要么实现。
2. 通常还有一个前提:存在覆盖。
多态的好处:
1.可替换性(substitutability)。多态对已存在代码具有可替换性。例如,多态对圆Circle类工作,对其他任何圆形几何体,如圆环,也同样工作。
2.可扩充性(extensibility)。多态对代码具有可扩充性。增加新的子类不影响已存在类的多态性、继承性,以及其他特性的运行和操作。实际上新加子类更容易获得多态功能。例如,在实现了圆锥、半圆锥以及半球体的多态基础上,很容易增添球体类的多态性。
3.接口性(interface-ability)。多态是超类通过方法签名,向子类提供了一个共同接口,由子类来完善或者覆盖它而实现的。如图8.3所示。图中超类Shape规定了两个实现多态的接口方法,computeArea()以及computeVolume()。子类,如Circle和Sphere为了实现多态,完善或者覆盖这两个接口方法。
4.灵活性(flexibility)。它在应用中体现了灵活多样的操作,提高了使用效率。
5.简化性(simplicity)。多态简化对应用软件的代码编写和修改过程,尤其在处理大量对象的运算和操作时,这个特点尤为突出和重要
1.重载overloading
1) 方法重载是让类以统一的方式处理不同类型数据的一种手段。多个同名函数同时存在,具有不同的参数个数/类型。重载是一个类中多态性的一种表现。
2) Java的方法重载,就是在类中可以创建多个方法,它们具有相同的名字,但具有不同的参数和不同的定义。调用方法时通过传递给它们的不同参数个数和参数类型来决定具体使用哪个方法, 这就是多态性。
3) 重载的时候,方法名要一样,但是参数类型和个数不一样,返回值类型可以相同也可以不相同。无法以返回型别作为重载函数的区分标准。
overload(重载)
1、参数类型、个数、顺序至少有一个不相同。
2、不能重载只有返回值不同的方法名。
3、存在于父类和子类、同类中。
示例代码:
public class Dog {
Dog() {
this.bark();
}
void bark()//bark()方法是重载方法
{
System.out.println("nobarking!");
this.bark("female",3.4);
}
void bark(String m,double l)//注意:重载的方法的返回值都是一样的,
{
System.out.println("abarking dog!");
this.bark(5,"China");
}
void bark(int a,String n)//不能以返回值区分重载方法,而只能以“参数类型”和“类名”来区分
{
System.out.println("ahowling dog");
}
public static void main(String[]args)
{
Dog dog = new Dog();
//dog.bark();
//dog.bark("male","yellow");
//dog.bark(5, "China");
}
}
2)重写overriding
1)父类与子类之间的多态性,对父类的函数进行重新定义。如果在子类中定义某方法与其父类有相同的名称和参数,我们说该方法被重写 (Overriding)。在Java中,子类可继承父类中的方法,而不需要重新编写相同的方法。但有时子类并不想原封不动地继承父类的方法,而是想作一定的修改,这就需要采用方法的重写。方法重写又称方法覆盖。
2) 若子类中的方法与父类中的某一方法具有相同的方法名、返回类型和参数表,则新方法将覆盖原有的方法。如需父类中原有的方法,可使用super关键字,该关键字引用了当前类的父类。
override(重写)
1、方法名、参数、返回值相同。
2、子类方法不能缩小父类方法的访问权限。
3、子类方法不能抛出比父类方法更多的异常(但子类方法可以不抛出异常)。
4、存在于父类和子类之间。
5、方法被定义为final不能被重写
6. 子类函数的访问修饰权限不能少于父类的;
示例代码:
public class Base
{
void test(int i)
{
System.out.print(i);
}
void test(byte b)
{
System.out.print(b);
}
}
public class TestOverriding extends Base
{
void test(int i)
{
i++;
System.out.println(i);
}
public static void main(String[]agrs)
{
Base b=new TestOverriding();
b.test(0)
b.test((byte)0)
}
}
这时的输出结果是1 ,0,这是运行时动态绑定的结果。
总结:面向对象是一种很自然,朴素的方法,来源于生活。
Pointp = new Point();的含义是首先实例化一个对象(new Point()),然后定义一个引用,p指向这个实例,p = p1;并不是将实例p1赋值给实例p,而是改变了引用的关系。
栈:内存和地址 堆: new的对象(先进后出)
相当于初始化
构造函数,是在对象实例化时自动被系统调用,该函数名必须是独一无二的。对与一个类来说,是将类名作为函数名。
构造函数不需要程序员去定义返回值,他是系统自动决定的,void也不行
Final修饰的方法不能被重写
Final修饰的属性不能被修改(也就是常量)
Final修饰的类不能被继承
this关键字是什么意思?
有时一个方法需要引用调用它的对象。为此,java定义了this这个关键字。简单地说,
This是在对象内部指代自身的引用。可以直接引用对象,可以解决实例变量和局部变量之间发生的任何同名的冲突。
没有加static关键词的成员变量只能面向这个对象,不能面向整个类
Static 静态:静态代码块是在加载类的时候自动被执行,早于构造函数,静态的方法,面向的是整个类,不是某个对象,所以没有this,并且只能使用类名来调用,不能使用实例化对象去调用
private static int n1,n2;
如 n1++自增1,没有static关键词修饰就只会是1,不会自增1
提取事物的本质,共性的属性和方法,忽略某些不必要的细节和个性的差异
为什么使用抽象类?
在现实生活中,有些类我们明明知道是有某些方法的,但是具体这些方法是怎麽去做的好像又无法说清楚,比如手机、电报、固定电话等都是通讯工具,而通讯工具就应该具有向外发送消息的方法,但是却无法说明具体怎麽发,而手机、电报等我们又可以说清楚他们是怎麽发送消息的
由于抽象类中包含有未实现的方法,所以抽象类是无法去实例化对象(new),那么它的作用是什么呢,它可以对它的后代进行规划和约束,后代必须去按照它的要求去实现,但具体怎麽实现由后代去决定
抽象方法:
只有声明,没有实现。abstract修饰抽象方法
抽象类的特点:
1、抽象类不能直接实例化,并且对抽象类使用new 运算符是编译时错误,他只能作为父类被继承。虽然一些变量和值在编译时的类型可以是抽象的,但是这样的变量和值必须或者为null,或者含有对非抽象类的实例的引用(此非抽象类是从抽象类派生的)。
2、允许(但不要求)抽象类包含抽象成员。
3、抽象类不能被密封
,
作用是:
对后代进行规则,即凡是它的后代,必须实现他未实现的方法
比如说,形状肯定有计算周长和面积的方法,但是具体如何计算的,却无法描述,三角形有三角形计算的方法,圆有圆计算的方法,所以在形状这个类里只能声明这个方法,但是不能实现
子类继承抽象父类必须要重写,而继承普通父类想重写就重写,不想重写就不重写
赋值兼容性规则:
凡是需要使用父类对象的地方,都可以使用其公有子类去代替
如:凡是需要使用车(父类)的地方,都可以使用三轮车(子类)去代替
抽象类的使用原则:
· 1.抽象类必须有子类,子类利用extends关键字来继承抽象类,一个子类只能够继承一个父类;
·2. 抽象类的子类(如果不是抽象类),那么必须要重写抽象类中的全部抽象方法;
3.抽象类可以利用对象的向上转型机制(赋值兼容性),通过子类对象进行实例化操作。
案例:
package类和对象.抽象类;
public abstract class Shape {
private Stringname;
public Shape(Stringname) {
this.name = name;
}
public StringgetName() {
returnname;
}
publicabstract double perimeter();
publicabstract double area();
}
三角形 子类
package类和对象.抽象类;
import java类和对象_案例.C201_02_02_坐标类.Point;
public class Triangle extends Shape {
private Pointp1;
private Pointp2;
private Pointp3;
public Triangle(Stringname, Point p1, Point p2, Point p3) {
super(name);
this.p1 = p1;
this.p2 = p2;
this.p3 = p3;
}
public Triangle(Stringname,double x1,double y1,double x2,double y2,double x3,double y3){
super(name);
this.p1 = new Point(x1,y1);
this.p2= new Point(x2,y2);
this.p3 = new Point(x3,y3);
}
@Override
publicdouble perimeter() {
doubles1 = p1.distance(p2);
doubles2 = p2.distance(p3);
doubles3 = p3.distance(p1);
returns1+s2+s3;
}
@Override
publicdouble area() {
doubles1 = p1.distance(p2);
doubles2 = p2.distance(p3);
doubles3 = p3.distance(p1);
doublea=(s1+s2+s3)/2;
doublearea = Math.sqrt(a*(a-s1)*(a-s2)*(a-s3));
returnarea;
}
}
圆 子类
package类和对象.抽象类;
public class Circle extends Shape {
privatedouble r;
public Circle(Stringname, double r) {
super(name);
this.r = r;
}
@Override
publicdouble perimeter() {
doubleperimeter=Math.PI* r *2;
returnperimeter;
}
@Override
publicdouble area() {
doublearea = Math.PI*Math.pow(r,2);
returnarea;
}
@Override
publicString toString() {
return"Circle{" +
"r=" + r+
'}';
}
}
测试类
publicclass Test_shape {
publicstatic void main(String[] args) {
Shape shape1 = new Circle("圆",5);//赋值兼容性规则
System.out.println(shape1.getName());
System.out.println(shape1.perimeter());
System.out.println(shape1.area());
shape1 = new Triangle("三角形",2,5,5,4,5,6);
System.out.println(shape1.area());
shape1 = new Square("正方形",5);
System.out.println(shape1.area());
}
}
接口所有的方法都是抽象方法:所以在声明方法时,关键词abstract写不写都没有关系了。
接口的定义关键词是interface,抽象类是class。
抽象类是用来继承(extends)的,接口是用实现(implements)的
匿名内部类:
为甚麽要使用匿名内部类
先看看如果不使用匿名内部类
(1) 定义接口Shape
public interface Shape {
public String getName();
public double perimeter();
public double area();
}
(2) 实现接口实现类Square
public class Square implements Shape{
private double d;
public Square(double d) {
this.d = d;
}
@Override
public String getName() {
return "正方形";
}
@Override
public double perimeter() {
return d*4;
}
@Override
public double area() {
return d*d;
}
}
(3) 定义Square类对象并使用
public class Test_jiekou {
public static void main(String[] args) {
Square square = new Square(3);
Shape shape = square;
print(shape);
}
public static void print(Shape shape){
System.out.println(shape.getName()+"的周长是"+shape.perimeter()+"\n面积是"+shape.area());
}
}
那如果不使用匿名内部类呢?
(1) 定义接口(与上一种情形一样)
(2) 定义匿名内部类,并实现接口(实际上就是将上一种情形的第2、3步给 合并了)
匿名内部类显然简化了流程,但是也使得使用者理解更加困难了,代码的层次不是太清晰。