1、继承
-
继承的概念
实现代码的重用,子类继承父类的一切属性和方法(除了构造方法),父类的私有属性、方法不能直接使用,需要get和set。
package com.test;
public class TestExtends {
public static void main(String[] args) {
Student s=new Student();
s.name="张三";
s.height=170;
s.major="qqq";
s.rest();
Student s1=new Student("历史",156,"计算机专业");
}
}
//-----------------------------
class Person1{
String name;
int height;
public void rest() {
System.out.println("休息一会!");
}
}
//----------------------------------
class Student extends Person1 {
String major;
public void study() {
System.out.println("学习一会儿!");
}
public Student() {}
public Student(String name,int height,String major) {
this.name=name;
this.height=height;
this.major=major;
}
}
- Java只有一个直接父类,接口可以多继承。
-
instanceof运算符
instanceof是二元运算符,左边是对象,右边是类;当对象是右面类或子类所创建对象时,返回true;否则,返回false。
System.out.print(s1 instanceof Person1);
//判断s1是不是Person1或者其子类创建的对象
-
继承的要点
1.父类也称作超类、基类、派生类等。
2.Java中只有单继承,没有像C++那样的多继承。多继承会引起混乱,使得继承链过于复杂,系统难于维护。
3.Java中类没有多继承,接口有多继承。
4.子类继承父类,可以得到父类的全部属性和方法 (除了父类的构造方法),但不见得可以直接访问(比如,父类私有的属性和方法)。
5.如果定义一个类时,没有调用extends,则它的父类是:java.lang.Object。
-
方法重写的三个要点
1.“==”: 方法名、形参列表相同。
2.“≤”:返回值类型和声明异常类型,子类小于等于父类。
3.“≥”: 访问权限,子类大于等于父类。
-
super关键字
super是直接父类对象的引用。可以通过super来访问父类中被子类覆盖的方法或属性。
-
继承树追溯
属性/方法查找顺序:(查找变量h)
- 查找当前类中有没有属性h。
- 依次上溯每个父类,查看每个父类中是否有h,知道object。
- 如果没有找到,则出现编译错误。
- 上面步骤中,找到h变量过程终止。
构造方法调用顺序:
先向上追溯到Object,然后再依次向下执行类的初始化块和构造方法,直到当前子类为止。
2、Object类
Object类是所有Java类的根基类,也就意味着所有的Java对象都拥有Object类的属性和方法。如果在类的声明中未使用extends关键字指明其父类,则默认继承Object类。
-
Object类的toString()方法
-
源码
public String toString() { return getClass().getName() + "@" +Integer.toHexString(hashCode()); }
-
-
==和equals
- ==:比较基本数据类型的值相同,引用数据类型的地址相同。
- equals:默认比较两个对象的hashcode,是同一个对象的引用时返回 true 否则返回 false。
3、封装
-
优点
-
提高代码的安全性。
-
提高代码的复用性。
-
“高内聚”:封装细节,便于修改内部代码,提高可维护性。
-
“低耦合”:简化外部调用,便于调用者使用,便于扩展和协作。
-
-
java中4种访问控制符
修饰符 同一个类 同一个包中 子类 所有类 private √ default √ √ protected √ √ √ public √ √ √ √ -
类的属性的处理
-
一般使用private访问权限。
-
提供相应的get/set方法来访问相关属性,这些方法通常是public修饰的,以提供对属性的赋值与读取操作(注意:boolean变量的get方法是is开头!
getName——》isName )。
- 一些只用于本类的辅助性方法可以用private修饰,希望其他类调用的方法用public修饰。
-
4、多态(polymorohism)
-
多态是方法的多态,不是属性的多态(多态与属性无关)。
- 多态的存在要有3个必要条件:继承,方法重写,父类引用指向子类对象。
-
父类引用指向子类对象后,用该父类引用调用子类重写的方法,此时多态就出现了。
- 多态的主要优势是提高了代码的可扩展性,符合开闭原则。但是多态也有弊端,就是无法调用子类特有的功能。
5、对象的转型(castint)
父类引用指向子类对象,我们称这个过程为向上转型,属于自动类型转换。
向上转型后的父类引用变量只能调用它编译类型的方法,不能调用它运行时类型的方法。这时,我们就需要进行类型的强制转换,我们称之为向下转型!
6、final关键字的作用
-
修饰变量:被修饰的变量变成常量。一旦赋值不能修改。
-
修饰方法:被修饰的方法不能被子类重写,能被重载。
-
修饰类:被修饰的类不能被继承。比如Math、String。
7、抽象方法和抽象类
-
抽象方法
使用abstract修饰的方法,没有方法体,只有声明。定义的是一种“规范”,就是告诉子类必须要给抽象方法提供具体的实现。
-
抽象类
包含抽象方法的类就是抽象类。通过abstract方法定义规范,然后要求子类必须定义具体实现。通过抽象类,我们就可以做到严格限制子类的设计,使子类之间更加通用。
-
抽象类使用要点:
- 有抽象方法的类只能定义成抽象类
- 抽象类不能实例化,即不能用new来实例化抽象类。
- 抽象类可以包含属性、方法、构造方法。但是构造方法不能用来new实例,只能用来被子类调用。
- 抽象类只能用来被继承。
- 抽象方法必须被子类实现。
8、接口
-
接口的作用
接口就是规范,是两个模块之间的通信标准。
-
接口和抽象类的区别
- 普通类:具体实现。
- 抽象类:具体实现,规范(抽象方法)。
- 接口:规范。
-
接口声明格式
[访问修饰符] interface 接口名 [implement 父接口1,父接口2…] { 常量定义; 方法定义; }
-
定义接口的详细说明
- 访问修饰符:只能是public或者默认。
- 接口名:和类名采用同样的命名机制。
- implement :接口可以多继承。
- 常量:接口中的属性只能是常量,用
public static final
修饰。 - 方法:接口中的方法只能是
public abstract
。
要点:
-
子类通过implements来实现接口中的规范。
-
接口不能创建实例,但是可用于声明引用变量类型。
-
一个类实现了接口,必须实现接口中所有的方法,并且这些方法只能是public的。
-
JDK1.7之前,接口中只能包含静态常量、抽象方法,不能有普通属性、构造方法、普通方法。
-
JDK1.8后,接口中包含普通的静态方法。
-
面向接口编程
通过面向接口编程,而不是面向实现类编程,可以大大降低程序模块间的耦合性,提高整个系统的可扩展性和和可维护性。
9、内部类
-
示例
/**外部类Outer*/ class Outer { private int age = 10; public void show(){ System.out.println(age);//10 } /**内部类Inner*/ public class Inner { //内部类中可以声明与外部类同名的属性与方法 private int age = 20; public void show(){ System.out.println(age);//20 } } }
__注意:__内部类只是一个编译时概念,一旦我们编译成功,就会成为完全不同的两个类。对于一个名为Outer的外部类和其内部定义的名为Inner的内部类。编译完成后会出现Outer.class和Outer$Inner.class两个类的字节码文件。所以内部类是相对独立的一种存在,其成员变量/方法名可以和外部类的相同。
-
内部类的作用
-
内部类提供了更好的封装。只能让外部类直接访问,不允许同一个包中的其他类直接访问。
-
内部类可以直接访问外部类的私有属性,内部类被当成其外部类的成员。 但外部类不能访问内部类的内部属性。
-
接口只是解决了多重继承的部分问题,而内部类使得多重继承的解决方案变得更加完整。
-
-
内部类使用场景
-
由于内部类提供了更好的封装特性,并且可以很方便的访问外部类的属性。所以,在只为外部类提供服务的情况下可以优先考虑使用内部类。
-
使用内部类间接实现多继承:每个内部类都能独立地继承一个类或者实现某些接口,所以无论外部类是否已经继承了某个类或者实现了某些接口,对于内部类没有任何影响。
-
-
内部类分类
1、非静态内部类
(1)非静态内部类必须寄存在一个外部类对象里。
(2)非静态内部类可以直接访问外部类的成员,但是外部类不能直接访问非静态内部类成员。
(3)非静态内部类不能有静态方法、静态属性和静态初始化块。
(4)外部类的静态方法、静态代码块不能访问非静态内部类,包括不能使用非静态内部类定义变量、创建实例。
-
(5)成员变量访问
- 内部类里面方法的局部变量:变量名。
- 内部类属性:this.变量名。
- 外部类属性:外部类名.this.变量名。
class Outer { private int age = 10; class Inner { int age = 20; public void show() { int age = 30; System.out.println("内部类方法里的局部变量age:" + age);// 30 System.out.println("内部类的成员变量age:" + this.age);// 20 System.out.println("外部类的成员变量age:" + Outer.this.age);// 10 } } }
-
内部类访问
-
外部类中定义内部类:
new Inner();
-
外部类以外的地方使用非静态内部类:
Outer.Inner varname=new Outer().new Inner();
-
2、静态内部类
static class ClassName { //类体 }
-
当一个静态内部类对象存在,并不一定存在对应的外部类对象。 因此,静态内部类的实例方法不能直接访问外部类的实例方法。
-
静态内部类看做外部类的一个静态成员。 因此,外部类的方法中可以通过:“静态内部类.名字”的方式访问静态内部类的静态成员,通过 new 静态内部类()访问静态内部类的实例。
3、匿名内部类
适合那种只需要使用一次的类。比如:键盘监听操作等等。
语法:
new 父类构造器(实参类表) \实现接口 () { //匿名内部类类体! }
注意:
1. 匿名内部类没有访问修饰符。 2. 匿名内部类没有构造方法。因为它连名字都没有那又何来构造方法呢。
4、局部内部类
局部内部类的的使用主要是用来解决比较复杂的问题,想创建一个类来辅助我们的解决方案,到那时又不希望这个类是公共可用的,所以就产生了局部内部类。局部内部类和成员内部类一样被编译,只是它的作用域发生了改变,它只能在该方法中被使用,出了该方法就会失效。
public class Test2 { public void show() { //作用域仅限于该方法 class Inner { public void fun() { System.out.println("helloworld"); } } new Inner().fun(); } public static void main(String[] args) { new Test2().show(); } }
-
10、String
- String类又称作不可变字符序列。
- String位于java.lang包中,Java默认导入Java.lang包下所有类。
- Java字符串就是Unicode字符序列。
- Java没有内置的字符串类型,而是在标准Java类库中提供了一个预定义的类String,每个用双引号括起来的字符串都是String类的一个实例。
- String字符串的拼接可以使用“+”。
-
全局字符串常量池(String Pool)
全局字符串常量池中存放的内容实在类加载完成后存到String Pool中,在每个VM中只有一份,存放的是字符串常量的引用值。(在堆中生成字符串对象实例)
-
class文件常量池(Class Constant Pool)
class常量池是在编译阶段每个class都有的,存放常量(文本字符串、final常量)、符号引用。
-
运行时常量池(Runtime Constant Pool)
运行时常量池是在类加载完成之后,将每个class常量池中的符号引用值转存到运行时常量池中,也就是说,每个class都有一个运行时常量池,类在解析之后,将符号引用替换成直接引用,与全局常量池中的引用值保持一致。
-
String类常用方法列表
- 字符串相等判断
- equals方法检测字符串内容相同。
"hello".equals("hello")
- 忽略大小写字符串相同判断:
"hello."equalsIgnore Case("HELLO")
- 不能使用
==
/**
*String类相关测试
**/
package com.zry.test1;
public class TestString {
public void Test1() {
String s1="hello";
String s2="Hello";
System.out.println("s1字符串下标为3的字符是:"+s1.charAt(3));
System.out.println("s2字符串的长度为:"+s2.length());
System.out.println("s1=s2?:"+s1.equals(s2));
System.out.println("忽略大小写s1=s2?:"+s1.equalsIgnoreCase(s2));
System.out.println("判断字s1符串中o第一次出现的位置:"+s1.indexOf("o"));
System.out.println("判断是s1字符串中没有s返回值为-1?:"+s1.indexOf("s"));
System.out.println("将s1字符串中是e替换为&,"+s1.replace("e", "&"));
}
public void Test2() {
String s="";
String s1="How are you?";
System.out.println("s1是否以How开头:"+s1.startsWith("How"));
System.out.println("s1是否以you结尾:"+s1.endsWith("you"));
System.out.println("提取s1字符串中下标4以后的字符:"+s1.substring(4));
System.out.println("提取s1字符串4-7(不包括7):"+s1.substring(4, 7));
System.out.println("转小写:"+s1.toLowerCase());
System.out.println("转大写:"+s1.toUpperCase());
String s2=" Hello old are you! ";
System.out.println("s2="+s2);
System.out.println("去除s2字符串首尾空格:"+s2.trim());
}
public static void main(String[] args) {
TestString ts=new TestString();
ts.Test1();
System.out.println("=====================================");
ts.Test2();
}
}
11、开闭原则
开闭原则(Open-Closed Principle)就是让设计的系统对扩展开放,对修改封闭。
-
对扩展开放
增加新功能时,不需要修改已有的代码,增加新代码就行。
-
对修改关闭
核心部分不随着需求改变而改变。
12、模板方法模式和回调机制
模板方法模式很常用,其目的是在一个方法中定义一个算法骨架,而将一些步骤延迟到子类中。模板方法使得子类可以在不改变算法结构的情况下,重新定义算法的某些步骤。在标准的模板方法模式实现中,主要是使用继承的方式,来让父类在运行期间可以调用到子类的方法。
回调是一种双向的调用模式,也就是说,被调用的接口被调用时也会调用对方的接口,简单点说明就是:A类中调用B类中的C方法,然后B类中的C方法中反过来调用A类中的D方法,那么D这个方法就叫回调方法。
回调具体过程:
-
Class A实现接口CallBack —— 背景1
-
class A中包含class B的引用 ——背景2
- class B有一个参数为CallBack的方法C ——背景3
-
前三条是我们的准备条件,接下来A的对象调用B的方法C
-
然后class B就可以在C方法中调用A的方法D
/** * 回调接口 */ interface CallBack { /** * 小高知道答案后告诉小刘时需要调用的方法,即回调方法 * @param result 是问题的答案 */ public void answer(String result); } /** * 小刘类:实现了回调接口CallBack(背景一) *实现接口的a类 */ class Liu implements CallBack { /** * 包含小高对象的引用 (背景二) */ private Gao gao; public Liu(Gao gao){ this.gao = gao; } /** * 小刘通过这个方法去问小高 * @param question 小刘问的问题“学习Java选哪家机构呢?” * d方法 */ public void askQuestion(String question){ //小刘问小高问题 gao.execute(Liu.this, question); } /** * 小高知道答案后调用此方法告诉小刘 */ @Override public void answer(String result) { System.out.println("小高告诉小刘的答案是:" + result); } } /** * 小高类 * 包含c方法的b类 */ class Gao { /** * 相当于class B有一个参数为CallBack的方法C(背景三) */ public void execute(CallBack callBack, String question){ System.out.println("小刘问的问题是:" + question); //模拟小高挂点后先办自己的事情花了很长时间 try { Thread.sleep(10000); } catch (InterruptedException e) { e.printStackTrace(); } //小高办完自己的事情后想到了答案 String result = "学Java当然去aaa"; //小高打电话把答案告诉小刘,相当于class B 反过来调用class A 的D方法 callBack.answer(result); } } public class Test { public static void main(String[] args) { Gao gao= new Gao(); Liu liu = new Liu(gao); //小刘问问题 liu.askQuestion("学习Java选哪家机构呢?"); } }
13、组合模式
组合模式是将对象组合成树形结构以表示“部分-整体”的层次结构。组合模式使得用户对单个对象和组合对象的使用具有一致性。
14、练习题
一、选择题
1.使用权限修饰符(B)修饰的类的成员变量和成员方法,可以被当前包中所有类访问,也可以被它的子类(同一个包以及不同包中的子类)访问。(选择一项)
Apublic
B.protected
C.默认
D.private
2.以下关于继承条件下构造方法执行过程的代码的执行结果是( A)。(选择一项)
class Person {
public Person() {
System.out.println("execute Person()");
}
}
class Student extends Person {
public Student() {
System.out.println("execute Student() ");
}
}
class PostGraduate extends Student {
public PostGraduate() {
System.out.println("execute PostGraduate()");
}
}
public class TestInherit {
public static void main(String[] args) {
new PostGraduate();
}
}
A.execute Person()
execute Student()
execute PostGraduate()
B.execute PostGraduate()
C.execute PostGraduate()
execute Student()
execute Person()
D.没有结果输出
3.编译运行如下Java代码,输出结果是(D )。(选择一项)
class Base {
public void method(){
System.out.print ("Base method");
}
}
class Child extends Base{
public void methodB(){
System.out.print ("Child methodB");
}
}
class Sample {
public static void main(String[] args) {
Base base= new Child();
base.methodB();
}
}
A.Base method
B.Child methodB
C.hild methodB
D.编译错误
4.在Java中关于abstract关键字,以下说法正确的是( AB)。(选择二项)
A.abstract类中可以没有抽象方法
B.abstract类的子类也可以是抽象类
C.abstract方法可以有方法体
D.abstract类可以创建对象
5.在Java接口中,下列选项中属于有效的方法声明是(AC )。(选择二项)
A.public void aMethod( );
B.final void aMethod( );
C.void aMethod();
D.private void aMethod( );
二、简答题
-
private、默认、protected、public四个权限修饰符的作用。
private私有属性 只有自己能够使用
defult 默认属性 同包类,同一包的方法,类可以使用
protected 受保护的 同一个包的还有不同包的子类访问
public 公开属性 所有的包和类都可以访问
-
继承条件下子类构造方法的执行过程。
父类非抽象类:调用父类的构造方法,然后调用子类自己的构造方法(如果有)
父类为抽象类:(无参)调用父类的无参构造方法,然后调用自己的构造方法(如果有)
(有参)子类显示申明调用父类的有参构造函数,然后在调用子类的构造方法(如果有)
-
什么是向上转型和向下转型。
向上转型: 父亲 f=new 孩子();
面向对象中的转型只会发生在有继承关系的子类和父类中(接口的实现也包括在这里)。
加入有父类:人,子类:男人和女人。
向上转型: Person p = new Man() ; //向上转型不需要强制类型转化
向下转型: Man man = (Man)new Person() ; //必须强制类型转化 -
final和abstract关键字的作用。
final是为了封装而生的,final父类方法一旦申明,子类只能继承无法修改,用于具有普适性的方法和变量。final类无法被继承.
abtract是为了抽象而生的,abstract类的子类只要不是abstract类,必须重写父类的abstract类.abstract方法不允许实现(没有方法体)
-
==和equals()的联系和区别。
==:用来比较基本类型数值相等,和引用数据类型地址相同。
equals():用来比较内容相同。
equals()的底层也是==实现的。
三、编码题
-
编写应用程序,创建类的对象,分别设置圆的半径、圆柱体的高,计算并分别显示圆半径、圆面积、圆周长,圆柱体的体积。
实现思路及关键代码:
(1)编写一个圆类Circle,该类拥有:
a) 一个成员变量,radius(私有,浮点型);//存放圆的半径
b) 两个构造方法(无参、有参);
c) 三个成员方法
double getArea() //获取圆的面积
double getPerimeter() //获取圆的周长
void show() //将圆的关径、周长、面积输出到屏幕
package com.zry.day05.lx1;
public class Circle {
private double radius;//圆的半径
public Circle(double radius) {
super();
this.radius = radius;
}
public Circle() {
super();
// TODO Auto-generated constructor stub
}
public double getArea() {//获取圆的面积
double area=Math.PI*radius*radius;
return area;
}
public double getPerimeter() {//获取圆的周长
double perimeter=Math.PI*2*radius;
return perimeter;
}
public void show() {//将圆的关径、周长、面积输出到屏幕
System.out.printf("圆的半径:radius=%.2f ,"
+ "周长:perimeter=%.2f,"
+ "面积:area=%.2f%n"
, radius,getPerimeter(),getArea());
}
}
(2)编写一个圆柱体类Cylinder,它继承于上面的Circle类。还拥有:
a) 一个成员变量,double hight (私有,浮点型); //圆柱体的高;
b) 构造方法
c) 成员方法
double getVolume() //获取圆柱体的体积
void showVolume() //将圆柱体的体积输出到屏幕
package com.zry.day05.lx1;
public class Cylinder extends Circle{
private double hight; //圆柱体的高
public Cylinder(double radius, double hight) {
super(radius);
this.hight = hight;
}
public double getVolume() {//获取圆柱体的体积
double volume=hight*getArea();
return volume;
}
public void showVolume() {//将圆柱体的体积输出到屏幕
System.out.println("圆柱体的体积:volume="+String.format("%.2f",getVolume()));
}
}
package com.zry.day05.lx1;
public class Test {
public static void main(String[] args) {
Circle c=new Circle(5);
c.show();
Cylinder cy=new Cylinder(5, 10);
cy.showVolume();
}
}
-
编写程序实现乐手弹奏乐器。乐手可以弹奏不同的乐器从而发出不同的声音。可以弹奏的乐器包括二胡、钢琴和琵琶。
实现思路及关键代码:
(1)定义乐器类Instrument,包括方法makeSound();
package com.zry.day05.lx2;
/**
* 这是一个乐器类,是所有乐器类的父类
* @author ZRY
*
*/
public abstract class Instrument {
public abstract void makeSound() ;
}
(2)定义乐器类的子类:二胡Erhu、钢琴Piano和小提琴Violin;
package com.zry.day05.lx2;
/**
* 这是一个二胡类
* @author ZRY
*
*/
public class Erhu extends Instrument{
@Override
public void makeSound() {
System.out.println("用二胡演奏二泉映月!!!");
}
}
package com.zry.day05.lx2;
/**
* 这是一个钢琴类
* @author ZRY
*
*/
public class Piano extends Instrument{
@Override
public void makeSound() {
System.out.println("用钢琴弹奏贝多芬的月光!!!");
}
}
package com.zry.day05.lx2;
/**
* 这是一个小提琴类
* @author ZRY
*
*/
public class Violin extends Instrument{
@Override
public void makeSound() {
System.out.println("用小提琴弹奏超级马里奥!!!");
}
}
(3)定义乐手类Musician,可以弹奏各种乐器play(Instrument i);
package com.zry.day05.lx2;
/**
* 这是一个乐手类
* @author ZRY
*
*/
public class Musician {
public void play(Instrument i) {
System.out.print("乐手A正在:");
i.makeSound();
}
}
(4)定义测试类,给乐手不同的乐器让他弹奏。
package com.zry.day05.lx2;
public class Test {
public static void main(String[] args) {
Musician m=new Musician();
m.play(new Erhu());
m.play(new Piano());
m.play(new Violin());
}
}
-
编写程序描述影视歌三栖艺人。需求说明:请使用面向对象的思想,设计自定义类,描述影视歌三梄艺人。
(1)分析影视歌三栖艺人的特性:可以演电影,可以演电视剧,可以唱歌
(2)定义多个接口描述特性
a) 演电影的接口-----方法:演电影
package com.zry.day05.lx; /** * 演电影的接口 * @author ZRY * */ public interface Movie { public void movie();//演电影方法 }
b) 演电视剧的接口-----方法:演电视剧
package com.zry.day05.lx; /** * 演电视剧的接口 * @author ZRY * */ public interface TV { public abstract void tv();//演电视剧方法 }
c) 唱歌的接口-----方法:唱歌
package com.zry.day05.lx; /** * 唱歌的接口 * @author ZRY * */ public interface Sing { public abstract void sing();//唱歌方法 }
(3)定义艺人类实现多个接口
package com.zry.day05.lx;
/**
* 艺人类
* @author ZRY
*
*/
public class Person implements Movie,TV,Sing{
String name;
@Override
public void sing() {
// TODO Auto-generated method stub
System.out.println("我会唱歌");
}
@Override
public void tv() {
// TODO Auto-generated method stub
System.out.println("我能演电视");
}
@Override
public void movie() {
// TODO Auto-generated method stub
System.out.println("我能演电影");
}
public Person(String name) {
super();
// TODO Auto-generated constructor stub
System.out.println("大家好,我是"+name);
}
}
package com.zry.day05.lx;
//测试类
public class Test {
public static void main(String[] args) {
Person p=new Person("张新成");
p.movie();
p.tv();
p.sing();
}
}