目录
一、面向对象三大特征
1、继承
继承是面向对象编程的三大特征之一,它让我们更加容易对于已有类的扩展、更加容易实现对于显现实世界的建模。
继承有两个主要作用:
1、代码复用,更加容易实现类的扩展。
2、方便对事物建模。
1.1继承的实现:
继承让我们更加容易实现类的拓展。比如,我们定义了人类,再定义boy就只需要扩展人类即可。实现了代码的重用。在java中使用extends来实现继承,子类是父类的扩展。在编程中,如果新定义一个Student类,发现已经有Person类包含了我们需要的属性和方法,那么Student类只需要继承Person类即可拥有Person类的属性和方法。
继承的使用举例:
package studyweek2;
class Person{
String name;
int height;
public void rest(){
System.out.println("休息一会");
}
}
class Student extends Person{//extend核心代码,把Person类继承到Students
String major;
public void study(){
System.out.println("赶快学习java");
}
public Student(String name,int height,String major){
this.name=name;
this.height=height;
this.major=major;
}
}
public class jicheng{
public static void main(){
Student s=new Student("ji",172,"java");
s.rest();
s.study();
}
}
除了构造方法之外的所有的属性和方法都被继承,但是不代表都能使用(比如private)
1.2instanceof运算符
instanceof是二元运算符,左边是对象,右边是类;当对象是右边的类或子类所创建对象时,返回true;否则,返回false。
1.3继承使用要点
单继承:每一个类只能有一个父类(可以有多个子类)。
1.4方法的重写override
子类通过重写的方法,可以用自身的行为替换父亲的行为。方法的重写是实现多态的必要条件。加简而言之就是在子类里重新进行定义实现覆盖。
方法的重写需要符合下面的三个要点:
1、"==":方法名、形参列表相同。
2、"<=":返回值类型和声明异常类型,子类小于等于父亲。
3、">=":访问权限,子类大于等于父类。
1.5final关键字
final关键字的作用:
1、修饰变量:被final修饰的变量不可改变。一旦赋了初值,就不能被重新赋值。例:final int a=100;
2、修饰方法:该方法不可被子类重写,但是可以被重载! 例:final void study(){}
3、修饰类:修饰的类不能被继承。比如Math、String。 例:final class a{}
1.6继承和组合
我们可以通过继承方便的复用已经定义类的代码。还有一种方式也可以方便的实现“代码复用”,那就是:“组合”。
组合不同于继承,更加灵活。
组成的核心就是:将父类对象作为子类的属性,然后子类通过调用这个属性来获得父类的属性和方法。
//组合的例子:
public class study3{
public static void main(String[] args){
Taidi c=new Taidi();//new一个Taidi类的对象,而Taidi类里的对象a,而a又是用Dog类new出来的,因此c有Dog类的所有属性。
c.a.height
c.a.shout();//不能直接用c.shout!
}
}
class Taidi{
Dog a=new Dog();//在Taidi这个类中new一个Dog类,a有了Dog类里的所有属性。
}
class Dog{
int height;
public void shout(){
System.out.println("冲冲冲");
}
}
2、Object类详解
2.1Object类基本特征
Object类是所有java类的根基类,也就意味着所有的java对象都拥有Object类的属性和方法。如果在类的声明中未使用extend关键字指明其直接父类,则默认继承Object类。
2.2toString方法
Object类中定义有public String toString()方法,其返回值是String类型。Object类中toString方法的源码为:
//重写toString()方法:@override
public String toString(){
return getClass().getName()+"@"+Integer.toHexString(hashCode())
}
public static void main(String[] args){
User u1=new User();
User u2=new User();
System.out.println(u1);
System.out.println(u2.toString());//与直接输出u2等价
}
不难看出,默认会返回类名+@+16进制的hashcode。在打印输出或者用字符串连接对象时,会自动调用该对象的toString()方法。如果要重写toString()方法,在开头加上@override
2.3"=="和equals方法
"="代表比较双方是否相同。如果是基本类型则表示值相等,如果是引用类型则表示地址相等即表示同一个对象。
Object类中定义有:public boolean equals(Object obj)方法,提供定义对象内容相等的逻辑。比如,我们在公安系统中认为id相同的人就是同一个人......
Object的equals方法默认就是比较两个对象的hashcode,是同一个对象的引用时返回true,否则返回false。但是,我们可以根据自己的要求重写equals方法。
System.out.println(u1==u2);//"=="就是判断是不是同一个对象。
System.out.println(u1.equals(u2));//equals判断两个对象的"逻辑上的值是不是一样",比如当两个人id一样我们就认为这两个是同一个对象
//可以使用alt加ins快速创建
//重写equals方法:
public boolean equals(Object obj){
if(obj==null){return false};
}
else{
if(obj instanceof Person){
Person c=(Person)obj;
if(c.id==this.id){retuen true;}
}
}
return false;
}
2.4super关键字
super可以看作是直接父类对象的引用,可以通过super来访问父类中被子类覆盖的方法和属性。
使用super调用普通方法,语句没有位置限制,可以在子类中随便调用。
class FatherClass{
public int value;
public void f(){
value=100;
System.out.println("FatherClass.value"+value);
}
}
class ChildClass extends FatherClass{
public int value;
public void f(){
super.f();//在子类中调用父类的普通方法,且super只能在子类中使用
value=200;
System.out.println("ChildClass.value"+value);
System.out.println(value);
System.out.println(super.value);//从这里可以看出重写并不会改变父类的属性和方法,而是会保留。
}
}
public class study3{
public static void main(String[] args){
new ChildClass().f(); //这种写法相当于下面两行代码的写法,只不过这种写法没有引用。
// ChildClass a=new ChildClass();
// a.f();
}
}
注意:若是构造方法的第一行代码没有显式的调用super()或者this(),那么java默认会调用super(),含义是调用父类的无参数构造方法。这里的super()可以省略。
class FatherClass2{
public FatherClass2(){
System.out.println("创建父类");
}
}
class ChildClass extends FatherClass2{
public ChildClass(){
//这里相当于加了一行代码: super();//第一句都是super()或者this(),因此都是父类先建好。
System.out.println("创建子类");
}
}
public class study3{
public static void main(String[] args){
System.out.println("进入程序");
new ChildClass();
}
}
注意!!!由于在子类构造器里一定会有super()去调用父类无参数构造方法,而如果此时父类中你已经定义了一个有参数的构造方法,那么父类就没有无参构造方了,编译就无法通过,解决方案有:
1、在子类构造器第一句使用super(参数) ;
2、在父类定义一个无参构造器(建议使用,并且建议只要是父类的都手动添加一个无参构造方法。)
下面是一个转载的资料链接:
父类在继承时一定要有无参构造器原因https://blog.csdn.net/weixin_46751741/article/details/121011395
3、封装
3.1封装的作用和含义
3.2封装的实现--使用访问控制符
protected使用的两个注意事项:
1、若父类和子类在同一个包中,子类可以访问父类的protected成员,也可以访问父类对象的protected成员。
2、若父类和子类不在同一个包中,子类可访问父类的protected成员,但不可以访问父类对象的protected成员。
3.3封装的使用细节以及get,set方法
get、set方法使用例子:
package studyweek2;
public class study4 {
private int age;
public String name;
private boolean flag;
public int getAge(){
return age;
}
public void setAge(int age){
this.age=age;
}
public String getName(){
return name;
}
public void setName(String name){
this.name=name;
}
public boolean isFlag(){//boolean类型不适用get,使用is开头。
return flag;
}
public void setFlag(boolean flag){
this.flag=flag;
}
public static void main(String[] args) {
study4 a=new study4();
a.setAge(20);
a.setName("jlk");
a.setFlag(true);
System.out.println(a.getAge());
System.out.println(a.getName());
System.out.println(a.isFlag());
}
}
4、多态
4.1多态的概念和实现
以下是使用多态的案例:
package studyweek2;
//多态
class Animals{
public void shout(){
System.out.println("叫了一声");
}
}
class Dog extends Animals {
@Override
public void shout() {
System.out.println("汪");
}
}
class Cat extends Animals{
@Override
public void shout() {
System.out.println("喵");
}
}
public class study5 {
//有了多态,只需要让增加的这个类继承Animal类就可以了。
static void animalCry(Animals a){//父类引用可以指向子类对象,而方法参数也是引用,故可以直接用父类作为参数,但是运行的时候使用的是运行类型,也就是后面的Cat和Dog类
a.shout();
}
public static void main(String[] args) {
Animals a1=new Cat();//父类引用指向子类对象。向上可以自动转型。
//传的具体是哪一个类就调用哪一个类的方法,大大提高了程序的可扩展性。
animalCry(a1);//运行类型是Cat,因此调用的时候就是使用Cat类的方法
Animals a2=new Dog();
animalCry(a2);
}
}
核心语句:父类 变量名=new 子类(),这里使用了父类引用指向了子类对象,由于是向上赋值,因此可以自动转型。具体赋值过程如下:
而如果不使用多态的话
static void animalCry(Dog a){
a.shout();
}
static void animalCry(Cat a){
a.shout();
}
//万一其他程序员再添加一个tiger类,又得重新定义一个参数为Tiger a的方法
static void animalCry(Tiger a){
a.shout();
}
以下说法会更好理解,在运行的时候都是用的时运行类型
补充事项:如果父类中定义了一个静态方法,子类中是无法对静态方法进行重写,而如果子类未对父类方法进行重写,那么父类引用指向子类对象时,父类引用所能看到的只是属于父类的那一部分属性和方法。如下列例子 :
public class StaticTest
{
public static void main(String[] args)
{
M m = new N();
m.output();//由于没有重写,运行时就只会按父类来运行,就不会按照子类。
}
}
class M
{
public static void output()
{
System.out.println("M");
}
}
class N extends M
{
public static void output()//父类含静态方法,而子类是无法重写静态方法的。
{
System.out.println("N");
}
}
向上转型:
向下转型:
4.2对象的转型
在上文中,父类引用指向子类对象的时候使用了对象的转型,因此在此进行详细说明
对象的转型分为向上转型和向下转型 。
父类引用指向子类对象,我们称之为向上转型,属于自动类型转换。而向上转型后的父类引用变量只能调用它编译类型的方法,不能调用它运行时候类型的方法。这时候,我们就需要进行类型的强制转换,我们称之为向下转型。
//其余代码与多态一致,另外假设在Dog类里面有个seeDoor方法,Cat类里面有个catchMouse方法。
Animal a=new Dog();//向上类型转换,自动的。
//a.seeDoor();//这个就会报错,无法调用,编译器只会认为你是Animal类,而不是Dog类。
Dog d2=(Dog) a;//强制类型转换,向下类型转型,把a这个Animal类型转换为Dog类型的d2使用。
d2.seeDoor();//可以运行。d2是Dog类。
//只有父类转为子类可以进行向下类型转换!!!
Cat c3=(Cat) a;//通过编译,但是不能运行,因为a是Dog类不能被强行转为Cat,真实的子类类型是Dog
c3.catchMouse();