面向对象的特性二继承:
为什么要有继承
多个类中存在相同属性和行为时,将这些内容抽取到单独一个类中, 那么多个类无需再定义这些属性和行为,只要继承那个类即可。
继承的好处:
1、减少代码的冗余、提高了代码的复用性 2、便于功能的扩展 3、让类与类之间出现了关系,为之后多态的使用,提供了前提
继承的格式:
class A extends B{} A:子类、派生类、subclass B:父类、超类、基类、superclass
体现:一旦子类A继承父类B以后,子类A中就获取了父类B中声明的所有的属性和方法。 特别的,父类中声明为private的属性或方法,子类继承父类以后,仍然认为获取了父类中私有的结构。 只有因为封装性的影响,使得子类不能直接调用父类的结构而已。 子类继承父类以后,还可以声明自己特有的属性或方法:实现功能的拓展。 子类和父类的关系,不同于子集和集合的关系。 extends:延展、扩展
java是只能单继承的 不可以多继承,
eg:
class A{} class B extends A{} class C extends B{} 如果c想要继承A和B那就要先继承B去继承A,这样c就有了B和A的所有内容了,不可以直接继承B和A
如果我们继承了一个类连这个类的私有属性也继承了只不过使用的时候要用set和get方法来获取
关于继承的规则: 子类不能直接访问父类中私有的(private)的成员变量和方法
practice
(1)定义一个ManKind类,包括 成员变量int sex和int salary;
方法void manOrWoman():根据sex的值显示“man”(sex==1)或者“woman”(sex==0);
方法void employeed():根据salary的值显示“no job”(salary==0)或者“ job”(salary!=0)。
(2)定义类Kids继承ManKind,并包括 成员变量int yearsOld;
方法printAge()打印yearsOld的值。
(3)定义类KidsTest,在类的main方法中实例化Kids的对象someKid,用该对象访问 其父类的成员变量及方法。
public class AprilNightTestOne { public static void main(String[] args) { Kind k = new Kind(); k.sex = 1; k.manOrWoman(k.sex); } } class ManKind{ int sex; int salary; public void manOrWoman(int sex){ if(sex == 1){ System.out.println("You are a man"); }else if(sex == 0){ System.out.println("You are a woman"); }else{ System.out.println("go away!"); } } public void employeed(int salary ){ if(salary == 0){ System.out.println("No job"); }else{ System.out.println("I have a very interesting jod"); } } } class Kind extends ManKind{ int yearsOld; public void printAge(){ System.out.println(yearsOld); } }
继承之后构造器注意点:
子类是不继承父类的构造器(构造方法或者构造函数)的,它只是调用(隐式或显式)。
如果父类的构造器带有参数没有无参构造器,则必须在子类的构造器中显式地通过 super 关键字调用父类的构造器并配以适当的参数列表。
如果父类构造器没有参数,则在子类的构造器中不需要使用 super 关键字调用父类构造器,系统会自动调用父类的无参构造器。
上图就是父类没有无参数构造器所以我们要使用super关键字调用父类构造器
上图解决方法:
public AprilSixAfternoonCheckAccount(int id, double balance, double annualInterestRate) { super(id, balance, annualInterestRate); } 必须是: 子类构造器(父类的参数在此定义一遍){ // 不定义父类中的参数 无法识别出 super(父类中的参数) }
方法的重写:
重写是基于继承的基础上
在子类中可以根据需要对从父类中继承来的方法进行改造,也称 为方法的重置、覆盖。在程序执行时,子类的方法将覆盖父类的方法。
要求:
1. 子类重写的方法必须和父类被重写的方法具有相同的方法名称、参数列表 2. 子类重写的方法的返回值类型不能大于父类被重写的方法的返回值类型 3. 子类重写的方法使用的访问权限不能小于父类被重写的方法的访问权限
> 子类不能重写父类中声明为private权限的方法 4. 子类方法抛出的异常不能大于父类被重写方法的异常
重写:当子类继承父类时,可以对父类中同名通参数的方法进行覆盖
重写之后当创建子类以后,通过子类调用父类中的同名同参数的方法时,实际执行的就是子类中重写的父类方法
public class ext { public static void main(String[] args) { C c = new C(); c.eat(); // 我是C中的eat方法 } } class A{ private int age; private String name; public void eat(){ System.out.println("我是A中的eat方法"); } } class B extends A{ //B继承 public void eat(){ System.out.println("我是B中的eat方法"); } } class C extends B{ public void eat(){ System.out.println("我是C中的eat方法"); } }
重写的规定:
方法的声明: 权限修饰符 返回值类型 方法名(形参列表){ //方法体 } 约定俗称:子类的叫重写方法,父类中的叫被重写的方法 1、子类重写的方法名和形参列表与父类被重写的方法的方法名和形参列表相同 2、子类重写的方法权限修饰符不能小于父类被重写的方法修饰符。>= 注意点: 子类中不能重写父类中私有(private)的方法 3、返回值类型: >父类被重写的方法返回值类型是void,则子类重写方法的返回值类型只能是void >父类被重写的方法返回值类型是A类型,则子类重写的方法返回值类型可以是A类或者A类的子类
>父类被重写的方法是基本数据类型,则子类重写的方法的返回值类型必须是相同的基本数据类型
4、子类重写的方法抛出的异常类型不大于父类被重写的方法抛出的异常
子类和父类中同名同通参数的方法要么都声明为非static(考虑重写),要么都声明为static(不是重写)静态方法不能被覆盖
eg:
class A{ private int age; private String name; public void eat(){ System.out.println("我是A中的eat方法"); } public static void ss(){ } } class B extends A{ //B继承 public void eat(){ System.out.println("我是B中的eat方法"); } // public void ss(){} // 父类中的方法是static子类的也需要是static的不然就报错 public static void ss(){} }
Practice
1.如果现在父类的一个方法定义成private访问权限,在子类中将此方 法声明为default访问权限,那么这样还叫重写吗?(NO)
2. 修改练习1.2中定义的类Kids,在Kids中重新定义employeed()方 法,覆盖父类ManKind中定义的employeed()方法,输出“Kids should study and no job.”
super关键字:
在Java类中使用super来调用父类中的指定操作:
super可用于访问父类中定义的属性
super可用于调用父类中定义的成员方法
super可用于在子类构造器中调用父类的构造器
尤其当子父类出现同名成员时,可以用super表明调用的是父类中的成员
super的追溯不仅限于直接父类
super和this的用法相像,this代表本类对象的引用,super代表父类的内存 空间的标识
super就是调用父类的方法时候使用的关键字,当子类重写了父类中的方法时候 我们还想再子类中继续使用父类中被重写的方法的时候就需要用到super了
public class ext { public static void main(String[] args) { C c = new C(); c.eat(); //输出: 我是C中的eat方法,我是B中的eat方法,我是A类中的show方法 } } class A{ private int age; private String name; public void eat(){ System.out.println("我是A中的eat方法"); } public static void ss(){ } public void show(){ System.out.println("我是A类中的show方法"); } } class B extends A{ //B继承 public void eat(){ System.out.println("我是B中的eat方法"); super.show(); // 调用父类中的show方法 } // public void ss(){} // 父类中的方法是static子类的也需要是static的不然就报错 public static void ss(){} public void show(){ System.out.println("我是B类中的show方法"); } } class C extends B{ public void eat(){ System.out.println("我是C中的eat方法"); super.eat(); // c类再执行自己继承的B类中的方法 } }
我们可以在子类的方法或者构造器中,通过使用“super.属性”或'super.'方法的方式,显示的调用 父类中声明的属性或方法,但是通常情况下 我们会省略这个“super”
super调用构造器
1 我们可以在子类的构造器中 使用 "super(形参)" 的方式来调用父类中对应的构造器 2 "super(形参列表)" 的方式调用父类的构造器必须声明在子类构造器的首行 3 我们再类的构造器中 针对 "this(形参列表)" 或者"super(形参列表)" 只能二选一 不能同时出现
4 在构造器的首行 没有显示的声明"this(形参列表)" 或者"super(形参列表)" 则默认调用的是父类中空参的构造器
5 在类中的多个构造器 至少有一个类的构造器中使用了 "super(形参列表)"调用父类中的构造器
如果父类中没有定义空参构造器继承这个父类的子类的所有构造器都要在子类自己的构造器中定义this(形参列表)或者super(形参列表)
class ParentSTest{
private int age;
private String name;
public ParentSTest(int age){
this.age = age;
}
public ParentSTest(String name){
this.name = name;
}
public void mm(){
System.out.println("This is ParentsTest's method");
}
}
class SonSTest extends ParentSTest{
String name;
int age;
public SonSTest(String name){
super(name); // 首行定义super(形参列表)
System.out.println("Test");
}
public SonSTest(int age,String name){
this(name); // 首行定义this(形参列表)
this.age = age;
}
public void mm(){
System.out.println("This is SonsTest's method");
}
}
如果子类不在自己的构造器首行定义this(形参列表)或者super(形参列表)就会提示此构造器不是父类默认的
综上如果父类没有定义空参构造器子类的所有构造器都要定义一个this(形参列表)或者super(形参列表)来解决
思考:
1).为什么super(…)和this(…)调用语句不能同时在一个构造器中出现?
2).为什么super(…)或this(…)调用语句只能作为构造器中的第一句出现?
1: 因为子类的this构造器都默认掉用了父类的空参构造器了,所以已经加载了父类的属性,如果在调用super就会出现加载重复了 因为this()里面还会调用(显式或者隐式调用)一次super(),如果允许,就会导致不容易发现的问题,如赋值被覆盖等等 2:也就是说你必须在构造器的第一行放置super或者this构造器,否则编译器会自动地放一个空参数的super构造器的,
其他的构造器也可以调用super或者this,调用成一个递归构造链,最后的结果是父类的构造器(可能有多级父类构造器)
始终在子类的构造器之前执行,递归的调用父类构造器。无法执行当前的类的构造器。也就不能实例化任何对象,这个类就成为一个无为类。 也就是你不在第一行放super或者this他就会默认调用父类的空参那样又是会出现重复调用了
Practices:
编写一个Student类,包含name、gender、age、id、score属性,分别为String、String、int、int、double类型。 类中声明一个say方法,返回String类型,方法返回信息中包含所有属性值。 在另一个StudentTest类中的main方法中,创建Student对象,并访问say方法和所有属性,并将调用结果打印输出。 编程题1 定义一个丈夫Husband类,有姓名、年龄、妻子属性 定义一个妻子Wife类,有姓名、年龄、丈夫属性 丈夫类中有一个getInfo方法,其中,能显示自己的姓名,年龄,和他的妻子的姓名,年龄 妻子类中有一个getInfo方法,其中,能显示自己的姓名,年龄,和她的丈夫的姓名,年龄 定义一个测试类,创建妻子和丈夫对象,然后测试 编程题2 定义银行账户类Account,有属性:卡号cid,余额balance,所属用户Customer 银行账户类Account有方法: (1)getInfo(),返回String类型,返回卡的详细信息 (2)取钱方法withdraw(),参数自行设计,如果取钱成功返回true,失败返回false (3)存钱方法save(),参数自行设计,如果存钱成功返回true,失败返回false 其中Customer类有姓名、身份证号、联系电话、家庭地址等属性 Customer类有方法say(),返回String类型,返回他的个人信息。 在测试类Bank中创建银行账户类对象和用户类对象,并设置信息,与显示信息 方法的使用 哪个选项和show()方法重载 class Demo{ void show(int a,int b,float c){} } A.void show(int a,float c,int b){}//yes B,void show(int a,int b,float c){}//一模一样。不可以出现在同一个类中。 C.int show(int a,float c,int b){return a;}//yes。 D.int show(int a,float c){return a;}//yes 方法的重载 方法重载(overload)必须满足________ A. 在不同class中定义的方法 B.在同一类型中定义的方法 C. 方法名必须相同 D.返回类型必须相同 E. 参数一定不同 F.参数可以相同 答案:BCE 写出输出结果 class Demo{ public static void main(String[] args){ show(0); show(1); } public static void show(int i){ switch(i){ default: i+=2; case 1: i+=1; case 4: i+=8; case 2: i+=4; } System.out.println("i="+i); } } i = 15 i = 14 因为从第一行开始匹配default是必须进去的 然后下面依次匹配然后每一次i的值都会相加 写出输出结果 class Demo{ public static void main(String[] args){ int x = 1; for(show('a'); show('b') && x<3; show('c')){ show('d'); x++; } } public static boolean show(char ch){ System.out.print(ch); return true; } }
面向对象
面向对象三大特征的说明
面向对象有三大特点:封装、继承、多态。(如果要回答四个,可加上 抽象性 这一特点) 1.继承性: 继承是一种联结类的层次模型,并且允许和鼓励类的重用,它提供了一种明确表述共性的方法。对象的一个新类可以从现有的类中派生,这个过程称为类继承。
新类继承了原始类的特性,新类称为原始类的派生类(子类),而原始类称为新类的基类(父类)。派生类可以从它的基类那里继承方法和实例变量,并且类可以修改或增加新的方法使之更适合特殊的需要。 2.封装性: 封装是把过程和数据包围起来,对数据的访问只能通过已定义的界面。面向对象计算始于这个基本概念,即现实世界可以被描绘成一系列完全自治、封装的对象,这些对象通过一个受保护的接口访问其他对象。 3. 多态性: 多态性是指允许不同类的对象对同一消息作出响应。多态性包括参数化多态性和包含多态性。多态性语言具有灵活、抽象、行为共享、代码共享的优势,很好的解决了应用程序函数同名问题。 4.抽象性: 抽象就是忽略一个主题中与当前目标无关的那些方面,以便更充分地注意与当前目标有关的方面。抽象并不打算了解全部问题,而只是选择其中的一部分,暂时不用部分细节。抽象包括两个方面,一是过程抽象,二是数据抽象。
作用域public,private,protected,以及默认不写时的区别
找错:
public class Something { void doSomething () { private String s = ""; int l = s.length(); } } 有错吗?
错,局部变量不可以用修饰符
Java的内存管理之垃圾回收
分配:由JVM自动为其分配相应的内存空间
释放:由JVM提供垃圾回收机制自动的释放内存空间
垃圾回收机制(GC:Garbage Collection):将垃圾对象所占用的堆内存进行回收。Java的垃圾回收机制是JVM提供的能力,由单独的系统级垃圾回收线程在空闲时间以不定时的方式动态回收。
垃圾对象:不再被任何引用指向的对象。
问:在程序中是否可以通知垃圾回收机制过来回收垃圾?
能,通过调用System.gc();或Runtime.getRuntime().gc();
再问:调用了System.gc();或Runtime.getRuntime().gc();后是立刻执行垃圾回收吗?
不是,该调用并不会立刻启动垃圾回收机制开始回收,但会加快垃圾回收机制的运行。
public class TestGC{
public static void main(String[] args)throws Exception{
for(int i=0; i<10; i++){
MyClass m = new MyClass();//这里本次循环完,本次创建的对象就成为垃圾了
System.out.println("创建第" + (i+1) + "的对象:" + m);
}
//通知垃圾回收机制来收集垃圾
System.gc();
//为了延缓程序结束
for(int i=0; i<10; i++){
Thread.sleep(1);
System.out.println("程序在继续....");
}
}
}
class MyClass{
//这个方法是垃圾回收机制在回收它的对象时,自动调用,理解成对象留临终遗言的方法
public void finalize(){
System.out.println("轻轻的我走了.....");
}
}
构造器:
构造器Constructor是否可被override
答:构造器Constructor不能被继承,因此不能重写Override,但可以被重载Overload
编程创建一个Box类,在其中定义三个变量表示一个立方体的长、宽和高,定义一个方法求立方体的体积。创建一个对象,求给定尺寸的立方体的体积。 (提供无参的构造器和一个有参的构造器) 定义一个圆类型 提供显示圆周长功能的方法 提供显示圆面积的方法 提供无参的构造器和一个有参的构造器 设计一个Dog类,有名字、颜色和年龄属性,定义构造器初始化这些属性,定义输出方法show()显示其信息。 提供无参的构造器和一个有参的构造器 定义一个类,用于描述坐标点 0——————>X | | | P(X,Y) | | Y (1)具有计算当前点到原点距离的功能 (2)求到任意一点(m,n)的距离 (3)求到任意一点(Point p)的距离 (4)具有坐标点显示功能,显示格式(x,y) (5)提供无参的构造器和一个有参的构造器
写一个人的类 属性:名字,性别,年龄;提供无参的构造器和一个有参的构造器 方法:(1)自我介绍的方法(2)吃饭的方法 创建一个对象“张三” 写一个汽车类: 属性:品牌;车长;颜色;价格; 创建五个对象:“捷达”,“宝马”,“劳斯莱斯”,“科鲁兹”,“迈锐宝” 提供无参的构造器和一个有参的构造器 写一个课程类: 属性:课程名;学时;任课老师; 创建五个对象:“c语言”,“java编程”,“php网络编程”,“c++”,“数据结构” 提供无参的构造器和一个有参的构造器
运行结果:
public class Test1 { public static void main(String[] args) { new A(new B()); } } class A{ public A(){ System.out.println("A"); } public A(B b){ this(); System.out.println("AB"); } } class B{ public B(){ System.out.println("B"); } }
答案:
B
A
AB
关于参数传递
写出结果。 public class Test{ public static void leftshift(int i, int j){ i+=j; } public static void main(String args[]){ int i = 4, j = 2; leftshift(i, j); System.out.println(i); } } 答案:4. 和leftShift函数没关系。因为函数结束后i又是4了
练习二
写出结果。
public class Demo{
public static void main(String[] args){
int[] a=new int[1];
modify(a);
System.out.println(a[0]); //
}
public static void modify(int[] a){
a[0]++;
}
}
答案: 1
public class TestA { int i ; void change(int i){ i++; System.out.println(i); } void change1(TestA t){ t.i++; System.out.println(t.i); } public static void main(String[] args) { TestA ta = new TestA(); System.out.println(ta.i); // 0 ta.change(ta.i);// 1 System.out.println(ta.i); // 0 ta.change1(ta); // 1 System.out.println(ta.i);// 1 } }
4
练习四 写出结果 class Value{ int i = 15; } class Test{ public static void main(String argv[]) { Test t = new Test(); t.first(); } public void first() { int i = 5; Value v = new Value(); v.i = 25; second(v, i); System.out.println(v.i); } public void second(Value v, int i) { i = 0; v.i = 20; Value val = new Value(); v = val; System.out.print(v.i + " " + i); } } 15 0 20
下面代码执行的结果是什么
public static void main(String[] args) { int i = 0; change(i); i = i++; System.out.println("i = " + i); } public static void change(int i){ i++; } 答案:i = 0
public static void main(String[] args) { String str = new String("world"); char[] ch = new char[]{'h','e','l','l','o'}; change(str,ch); System.out.println(str); System.out.println(String.valueOf(ch)); } public static void change(String str, char[] arr){ str = "change"; arr[0] = 'a'; arr[1] = 'b'; arr[2] = 'c'; arr[3] = 'd'; arr[4] = 'e'; } 答案: world abcde public class Test { int a; int b; public void f(){ a = 0; b = 0; int[] c = {0}; g(b,c); System.out.println(a + " " + b + " " + c[0]); } public void g(int b, int[] c){ a = 1; b = 1; c[0] = 1; } public static void main(String[] args) { Test t = new Test(); t.f(); } } 答案:1 0 1
简答 当一个对象被当作参数传递到一个方法后,此方法可改变这个对象的属性,并可返回变化后的结果,那么这里到底是值传递还是引用传递? 答:是值传递。Java 编程语言只有值传递参数。当一个对象实例作为一个参数被传递到方法中时,
参数的值就是对该对象的引用。对象的内容可以在被调用的方法中改变,但对象的引用是永远不会改变的
补足compare函数内的代码,不许添加其他函数。
class Circle { private double radius;
public Circle(double r) { radius = r; }
public Circle compare(Circle cir) { // 程序代码 /* * if(this.radius>cir.radius) return this; return cir; */
// return (this.radius>cir.radius)?this: cir;
} }
class TC { public static void main(String[] args) { Circle cir1 = new Circle(1.0); Circle cir2 = new Circle(2.0); Circle cir; cir = cir1.compare(cir2); if (cir1 == cir) System.out.println("圆1的半径比较大"); else System.out.println("圆2的半径比较大"); } } |
.
练习六
import java.util.Arrays; public class PassValueExer2{ public static void main(String[] args){ int[] array = {3,2,5,1,7};
//调用sort方法,实现从大到小排序 //在此处补充代码 ....
//显示结果 System.out.println("排序后的结果是:" + Arrays.toString(array)); }
//要求使用冒泡排序完成 public void sort(//形参?){
} } |
答案:
/* 考点: 1、方法的参数传递机制 2、冒泡排序 */ import java.util.Arrays; public class PassValueExer2{ public static void main(String[] args){ int[] array = {3,2,5,1,7}; PassValueExer2 exer = new PassValueExer2(); //调用sort方法,实现排序 exer.sort(array);//实参给形参的是地址,数组的首地址
//遍历结果 System.out.println("排序后的结果是:" + Arrays.toString(array)); }
//功能:用冒泡排序,实现为数组排序,而且从大到小 //形参的类型?我要把什么传过来 //传递数组 //接收的类型也肯定是数组,即形参接收实参,即形参的类型是int[] public void sort(int[] arr){ //冒泡排序 //在这里对谁排,对arr进行排序 for(int i=1; i<arr.length; i++){//多少轮 //每一轮,从左往后--》for(int j=0;...) //要实现从大到小-->前面的元素比后面的元素小,就交换 //每一轮几次, //假设,数组的长度为5 //第一轮:4次,i=1, j=0,1,2,3 j<4 j< arr.length-i //第二轮:3次,i=2, j=0,1,2 for(int j=0; j<arr.length-i; j++){ if(arr[j] < arr[j+1]){ int temp = arr[j]; arr[j] = arr[j+1]; arr[j+1] = temp; } } } } } |