黑马程序员————java基础---------面向对象(二)

来源:互联网 发布:百傲瑞达3.0软件 编辑:程序博客网 时间:2024/06/07 06:55

------Java培训、Android培训、iOS培训、.Net培训、期待与您交流! -------

面向对象(二)

一、代码块
用{}括起来的代码。代码块可以分为局部代码块,构造代码块,静态代码块。局部代码块,用与限定变量的生命周期,及早释放,提高内存利用率。构造代码块,把多个构造方法中相同的代码放到一起,每个构造方法执行前,首先执行构造代码块。静态代码块对类的数据进行初始化,仅仅只执行一次。在代码块的执行顺序中,静态代码块早于构造代码块早于构造方法。


class BlockDemo{//静态代码块static{int a=10;System.out.println(a);}//构造代码块{int b=20;System.out.println(b);}//构造代码块{int c=30;System.out.println(c);}//构造方法public BlockDemo(){System.out.println(40);}public BlockDemo(int d){System.out.println("北京黑马");}}class Demo{public static void main(String [] args){BlockDemo bd=new BlockDemo();BlockDemo bd1=new BlockDemo(1);}}


输出结果为:
10
20
30
40
20
30
北京黑马


注意:从输出结果可以看出,每次在创建对象时,构造代码块都会被执行。其中输出了20,30各两次。而静态代码块只是在类加载的时候,才会输出一次。无论是有参构造,还是无参构造,都会访问全部的构造代码块。




二、继承

将多个类中相同的成员变量或成员方法提取出来,定义到一个独立的类中,让后让拥有这些相同内容的类继承这个独立的类。这个独立的类,和每一个继承他的类就是继承关系。继承关系,不能多继承,只能多层继承。就是一个类只能继承一个类,但是还可以别的类来继承这个类,因此形成了多层继承关系。

继承的格式:class 子类名 extends 父类名{}

继承的优点:提高了代码的复用性,提高了代码的维护性,让类与类产生了一个关系,是多态的前提。

继承的注意事项:

1、子类不能继承父类的私有成员

2、子类不能继承父类的构造方法,但可以通过super去访问

3、不要为了部分功能而去继承


继承中成员关系:

1、成员变量:如果父类与子类的成员变量名相同,子类对象调用的是子类的成员变量。如果父类与子类的成员变量不同,子类方法首先在方法局部范围找,其次在子类的成员范围找,再次在父类的成员范围找。子类的方法不可能查询父类的方法体.

2、构造方法:子类的构造方法默认会去访问父类的无参构造方法。这是为了子类访问父类数据初始化。如果父类没有无参构造方法,子类通过super去明确调用带参构造方法。super必须在子类构造方法内部第一行。

3、成员方法:如果父类与子类的成员方法不一样,子类就会继承父类的方法,并且自己的特殊方法也可以调用。如果子类和父类的方法名、参数列表、返回值都相同,那么子类的方法就会覆盖父类的方法

class Animal{private String name;private int age;Animal(){}Animal(String name,int age){this.name=name;this.age=age;}public void setName(String name){this.name=name;}public String getName(){return name;}public void setAge(int age){this.age=age;}public int getAge(){return age;}public void eat(){System.out.println("小猫吃鱼");}}class Cat extends Animal{public void play(){System.out.println("小猫钓鱼");}}class AnimalDemo {public static void main(String [] args){Cat c=new Cat();c.play();c.eat();}}

输出结果为:

小猫钓鱼

小猫吃鱼


从这里结果中我们可以看到eat()方法在Cat类中没有定义,但是在Cat对象c却可以调用这个方法,这是因为在Cat类的父类Animal类中定义了eat()方法,由于Cat类继承了Animal类,因此也继承了eat()方法。另外Cat类还有自己的特有方法play().


方法重写:
class Cat extends Animal{public void eat(){System.out.println("小猫吃老鼠");}public void play(){System.out.println("小猫钓鱼");}}


对于上面这个例子,如果Cat类中也有eat(),并且参数列表也同父类相同,Cat类中的eat()方法就将Animal类中的eat()重写了。当Cat类的对象再次调用eat()时,就调用的是Cat类特有的方法了。




练习题:
如果子继承父,先运用的是父中的静态代码块,然后才是子中的静态代码块。
然后运行的是父中的构造代码块,构造函数,然后才是子中的构造代码块,构造函数。
class Fu{static {System.out.println("Fu-static");}{System.out.println("Fu-gzdmk");}Fu(){System.out.println("Fu-gzhs");}}class Zi extends Fu{static {System.out.println("Zi-static");}{System.out.println("zi-gzdmk");}Zi(){System.out.println("zi-gzhs");}}class Demo1{public static void main(String [] args){//主方法进栈,创建对象zZi z=new Zi();}}


输出结果为:
Fu-static
Zi-static
Fu-gzdmk
Fu-gzhs
zi-gzdmk

zi-gzhs


由此得出Java初始化顺序结论:
1 继承体系的所有静态成员初始化(先父类,后子类)
2 父类初始化完成(普通成员的初始化-->构造函数的调用)
3 子类初始化(普通成员-->构造函数)
子类要继承父类的方法,就要先初始化父类。初始化父类,就要调用父类的构造函数。


分层初始化:
class X{//这是第三步Y b=new Y();//这是第四步,先进行成员变量初始化。X(){//第五步,再进行构造函数初始化。这一步结束,X类的加载就结束了。System.out.println("x");}}class Y{Y(){System.out.println("y");}}class Z extends X{//这是第二步。从而找到父类X。Y y=new Y();//这是第六步,先进行成员变量的初始化Z(){//这是第七步,再进行构造函数的初始化。System.out.println("z");}public static void main(String [] args){new Z();//这是第一步}}


输出结果为:
y
x
y
z




在同一个类中成员变量的默认初始化,要早于成员变量的显示初始化,成员构造方法初始化在两者之后。
class Construct{Construct(){System.out.println("Construct的无参构造方法");}Construct(String name){System.out.println("Construct的有参构造方法");//这是第三步}}class ConstructDemo{ConstructDemo(){System.out.println("ConstructDemo的默认构造方法");//这是第四步。}Construct c=new Construct("li");//这里的同Y y=new Y();是一个道理,都是属于成员变量的地位,而且是引用变量。所以这里是第二步,要早于本类的默认构造。而他的初始化,又会调用Construct的有参构造.public static void main(String [] args){ConstructDemo c1=new ConstructDemo();//这是第一步。}}


输出结果为:
Construct的有参构造方法
ConstructDemo的默认构造方法




三、final关键字
final 类是最终的意思,可以修饰类、方法和变量。它修饰的类不能被继承,只能创建对象;它修饰的方法,不能被重写;它修饰的变量,为一个常量,不能重写被赋值。




四、多态
同一个对象在不同时刻体现出来的不同状态。多态的前提是有继承或者实现关系。有方法的重写,有父类或者父接口引用指向子类对象。多态的分类:
具体类多态:
class Fu{}class Zi extends Fu{}class Demo{public static void main(String [] args){Fu f=new Zi();}}


抽象类多态
abstract class Fu{}class Zi extends Fu{}class Demo{public static void main(String [] args){Fu f=new Zi();}}


接口多态
interface Fu{}class Zi implements Fu{}class Demo{public static void main(String [] args){Fu f=new Zi();}}


多态中的成员访问特点
A:成员变量
编译看左边,运行看左边
B:构造方法
子类的构造都会默认访问父类构造
C:成员方法
编译看左边,运行看右边
D:静态方法
编译看左边,运行看左边


多态中的转型:
向上转型:从子到父;
向下转型:从父到子;
在转型中我们要注意:类型转换错误在编译期不会报错,在运行期才会出错。假如有一个Animal类,Cat类和Dog类都分别继承了Animal类,那么:
Anima ac=new Cat();
Animal ad=new Dog()
都是成立的。并且我们可以Cat d=(Cat)ac;但是如果Cat d=(Cat)ad;这样就会在运行期报错。

class A{public void show(){show2();}public void show2(){System.out.println("我");}}class B extends A{public void show2(){System.out.println("爱");}}class C extends B{public void show(){super.show();}public void show2(){System.out.println("你");}}class AbcDemo{public static void main(String [] args){A a=new B();a.show();//此时执行show();而这个方法就是调用show2();而B中的show2又将A中的show2给重写了。所以结果是“爱”。B b=new C();b.show();//此时B中有从A中继承过来的show();所以B和C中都有show();而不会报错。执行的是C里面的show()方法,但是C里面的show方法调用了B里面的show方法。而B里面的show2(),又被C中的给覆盖了,因此打出来的是“你”。}}






五、抽象类
我们在将具有共性的东西提取到一个类后,然后将其他类来继承这个类,优化了代码,但是在实际操作中,每个对象在具体实现的时候内容是不一样的,比如猫和狗都具备eat()方法,但是猫和狗却在实现吃这个行为时表现不同。比如猫吃鱼,狗吃骨头。因此我们就不容易在父类中的eat()将具体内容规定出来。但是我们可以通过子类重写父类方法的方式,来实现这个要求。于是就有了抽象类。
抽象类的特点
A:抽象类和抽象方法必须用关键字abstract修饰
B:抽象类中不一定有抽象方法,但是有抽象方法的类一定是抽象类
C:抽象类不能实例化
D:抽象类的子类
a:是一个抽象类。
b:是一个具体类。这个类必须重写抽象类中的所有抽象方法。
抽象类的成员特点:
A:成员变量
有变量,有常量
B:构造方法
有构造方法
C:成员方法
有抽象,有非抽象




六、接口 
接口的特点:
A:接口用关键字interface修饰
interface 接口名 {}
B:类实现接口用implements修饰
class 类名 implements 接口名 {}
C:接口不能实例化
D:接口的实现类
a:是一个抽象类。
b:是一个具体类,这个类必须重写接口中的所有抽象方法。
接口的成员特点:
A:成员变量
只能是常量
默认修饰符:public static final
B:构造方法
没有构造方法
C:成员方法
只能是抽象的
默认修饰符:public abstract
类与类,类与接口,接口与接口
A:类与类
继承关系,只能单继承,可以多层继承
B:类与接口
实现关系,可以单实现,也可以多实现。
还可以在继承一个类的同时,实现多个接口
C:接口与接口
继承关系,可以单继承,也可以多继承


七、形式参数和返回值的问题
思考题 关于形式参数的改变对基本类型和引用类型数据的不同影响。
基本类型:形式参数的改变对实际参数没有影响。
引用类型:形式参数的改变对实际参数有影响。

class Args {public static void main(String[] args) {int a = 10;int b = 20;System.out.println("a:"+a+",b:"+b); //a:10,b:20System.out.println("********");change(a,b);//这里将会输出change方法中的两条输出语句,由于方法是void无返回值的,因此采用的是单独调用。由于是基本类型的方法,因此当其运行结束后就会直接出栈,不会影响change(a,b)之后的输出语句。System.out.println("%%%%%%%%%%%%%%%");System.out.println("a:"+a+",b:"+b); //???a:10,b:20System.out.println("&&&&&&");int[] arr = {1,2,3,4,5}; change(arr);//由于此前在堆里面定义了数组,因此在栈里面调用change方法时,就直接是main方法将地址传递给了change,然后change通过地址在堆里面找到了数组,然后进行运算,运算结束后,change出栈,但是main方法中的地址仍在。System.out.println(arr[1]); //???4}public static void change(int a,int b) { //a=10,b=20System.out.println("a:"+a+",b:"+b); //a:10,b:20a = b;//a=20b = a + b; //b=40System.out.println("a:"+a+",b:"+b); //a:20,b:40}public static void change(int[] arr) { //arr={1,2,3,4,5};for(int x=0; x<arr.length; x++) {if(arr[x]%2==0) {arr[x]*=2;}}//arr={1,4,3,8,5};}}


void:表示无返回值类型。当返回值不是一个明确的值的时候,即使是两个int值,也需要用void。当用了void之后,就只能单独输出。此时若希望单独输出查验结果,就需要在调用方法中进行输出语句。


基本类型形式参数问题
class Number {int i;}public class Assignment {public static void main(String[] args) {Number n1 = new Number();Number n2 = new Number();n1.i = 9;n2.i = 47;//System.out.println("1: n1.i: " + n1.i +", n2.i: " + n2.i);n1 = n2;//System.out.println("2: n1.i: " + n1.i +", n2.i: " + n2.i);n1.i = 27;//n1=n2;此时就将n2的地址值赋给了n1,因此n1.i=27这条语句中的27,实际上就是给了n2;因为n1.i的地址值实际上指向的是n2.i的地址。因此n2.i的值是27.而n1.i又指向了n2.i,因此两个值都是27.System.out.println("3: n1.i: " + n1.i +", n2.i: " + n2.i);}}




八、内部类:
(1)把类定义在另一个类的内部,该类就被称为内部类。

举例:把类B定义在类A中,类B就被称为内部类。


(2)内部类的访问规则
A:可以直接访问外部类的成员,包括私有

B:外部类要想访问内部类成员,必须创建对象


(3)内部类的分类
A:成员内部类

B:局部内部类


(4)成员内部类

A:private 为了数据的安全性
B:static 为了访问的方便性

成员内部类不是静态的:
外部类名.内部类名 对象名 = new 外部类名.new 内部类名();
成员内部类是静态的:

外部类名.内部类名 对象名 = new 外部类名.内部类名();


(5)成员内部类的练习题

class Outer {public int num = 10;class Inner {public int num = 20;public viod show() {int num  = 30;System.out.println(num);System.out.println(this.num);System.out.println(Outer.this.num);//在需要调用外部类的成员变量时,需要用途类名+this+变量名。或者new一个外部类对象也可以}}}

输出:30,20,10



------Java培训、Android培训、iOS培训、.Net培训、期待与您交流! -------
0 0
原创粉丝点击