一、继承、
1、继承
public class inherit {
public static void main(String[] args) {
//Students S = new Students();
//S.setName("Xiaohong");
//System.out.println(S.getName());
Scores Sc = new Scores();
Sc.setName("Xiaohong"); //可以访问父类的setName()方法
System.out.println(Sc.namess()); //
}
class Students{
//private String name; //用private 修饰的字段 name 和 age,无法被子类 Scores 访问,需将 private 换为 protected
//private int age;
protected String name; //protected 修饰的字段可以被子类 Scores 访问
protected String age;
public void setName(String name) {
this.name = name;
}
public void setAge(int age) {
this.age = age;
}
public String getName() {
return this.name;
}
public int getAge() {
return this.age;
}
}
class Scores extends Students{ //Scores 继承了 Studnets 类
private int score;
public String namess() {
//return "Hello, " + name;
//return "Hello, " + this.name;
return "Hello, " + super.name;
}
public void setScore(int score) {
this.score = score;
}
public int getScore() {
return score;
}
}
输出
若 Scores 类中包括了 Students 类中的 name,age 字段,通过继承的方式,在 Score 类中就不用重复的写入 name,age 字段了,只需要在 Scores 类中添加新的字段 score;其中的 Students 类通常称之为超类 super class ,父类 parent class ,几类 base class;把 Scores 类称之为子类 subclass,扩展类 extended class
2、继承树
注意:在上面定义的时候,Students 没有写 extends, 在java 中没有明确写 extends 的类,编译器会自动加上 extends Object,所以任何类除了 Object,都会继承某个类,如下
在java继承中,一个类只一个父类,Object 类比较特殊没有父类
3、protected
在继承中子类无法访问父类的 private 字段或 private 方法,为了让子类可以访问父类的字段,我们需要把 private 改为 protected,用 protected 修饰的字段可以被子类访问
4、super
super关键字表示父类(超类),子类引用父类的字段时,可以使用 super.字段名
...skip...
public String namess() {
//return "Hello, " + name;
//return "Hello, " + this.name;
return "Hello, " + super.name;
}
...skip...
这里使用 super.name ,或者 this.name ,或者 name ,其实效果一样的;但是在某些情况下必须要使用 super 字段,如下面的例子
public class Superfield {
...skip...
}
class Studentss{
protected String name;
protected int age;
public Studentss(String name,int age) {
this.name = name;
this.age = age;
}
...skip...
}
class Scoress extends Studentss {
protected int score;
public Scoress(String name, int age, int score){
super(name,age); //调用父类构造函数
this.score = score;
}
}
如果父类没有默认的构造方法,子类就必须显示调用 supre()并给入参数以便让编译器定位到父类的一个合适的构造方法,这里顺带引出了一个问题:就是子类不会继承父类的构造方法,子类默认的构造方法是编译器自动生成的,不是继承的
5、向上转型
实际上就是把一个子类型安全地变为更加抽象的父类型
public class inherit {
public static void main(String[] args) {
//Students S = new Students();
//S.setName("Xiaohong");
//System.out.println(S.getName());
Scores Sc = new Scores();
Students S = Sc;
S.setName("Xiaohong");
System.out.println(Sc.namess());
System.out.println(S.getName());
}
}
class Students{
...skip...
}
class Scores{
public String namess() {
return "Hello, " + name;
}
...skip...
}
输出:
6、向下转型
和向上转型相反,如果把一个父类类型强制转化为子类类型,就是向下转型
public class inherit {
public static void main(String[] args) {
Students S1 = new Scores(); //父类 Students S1 转为子类
Scores F1 = (Scores) S1;
S1.setName("Xiaohong");
System.out.println(F1 instanceof Scores); //true
System.out.println(S1.getName()); //Xiaohong
}
}
...skip...
向下转型时可能会失败,这时在向下转型时可以先进行判断,java中提供了 instanceof 操作符,来判断一个实例是不是某种类型,实际上是判断一个变量所指向的实例是否是指定类型,或者这个类型的子类;
...skip...
Students S1 = new Scores();
Scores F1 = (Scores) S1;
Students S2 = null;
System.out.println(F1 instanceof Students); //true
System.out.println(S1 instanceof Scores); //true
System.out.println(S2 instanceof Students); //false
...skip...
按理,如果在往下转型时可以添加判断
Students S1 = new Scores();
if (S1 instanceof Scores) {
//只有判断成功才会向下转型
Scores S2 = (Scores) S1;
S2.setName("Xiaofang");
System.out.println(S2.namess()); //Xiaofang
}
7、区分继承和组合:从逻辑上讲,父类与子类的关系是 is 的关系,因此继承是 is 的关系
二、多态
1、多态
Override 与 Overload
在继承的关系中,子类如果定义了一个与父类方法相同的方法,被称之位覆盖(Override);如果方法签名不同,就是Overload,Overload是一个新的方法;如果方法签名相同,并且放回值相同,就是override
注意:
① 方法名相同,方法参数相同,但是方法返回值不同,也是不同的方法
② 什么是方法签名呢?
方法签名就是,方法名(类型 参数)
public class polymorphic {
public static void main(String[] args) {
//...skip...
}
}
class Studentsss{
public void abc() {
System.out.println("123");
}
}
class fScores extends Studentsss{
@Override
public void abc() {
System.out.println("12123");
}
}
@Override 不是必须的,在上面笔记中,引用变量声明的类型可能与实际类型不符
public class dasdf {
public static void main(String[] args) {
Mans M = new Peoples();
M.names(); //2
}
}
class Mans{
protected String names;
public void names() {
System.out.println("1");
}
}
class Peoples extends Mans{
@Override
public void names() {
System.out.println("2");
}
}
上面中运行后发现调用的是 Peoples 中的 names() 方法,可以得出java的实例方法调用是基于运行时实际类型动态调用的,而非变量的声明类型,这个非常重用的特性在面向编程中称之为多态;而多态是指针对某个类型的方法调用,其正真执行的方法取决于实际类型的方法
对于多态的理解:简而言之就是,父类的对象变量调用了子类中的重写方法,需要注意的是,通常是自有一个父类,而他有多个子类,且在这些子类中同事重写父类的某个方法;多态的前提是有一个父类多个子类。
多态的简单理解: 如有游戏中,技能键,如按 R 的指令,在不同英雄或人物发出的技能效果就不一样
如:
public class dasdf {
public static void main(String[] args) {
Father F1 = new Father();
Father F2 = new Sonone(); //前后两类型不同就产生了多态(就是说继承关系的类型,前者一定是后面的父类)
Father F3 = new Sontwo();
Sontwo S1 = new Sontwo();
F1.names(); //1
F2.names(); //2
F3.names(); //3
F3.newFunction(); //报错
S1.newFunction(); //4
}
}
class Father{
protected String names;
public void names() {
System.out.println("1");
}
}
class Sonone extends Father{
@Override
public void names() { //重写父类方法
System.out.println("2");
}
}
class Sontwo extends Father{
public void names() { //重写父类方法
System.out.println("3");
}
public void newFunction(){
System.out.println("4");
}
}
覆写Object
因为所有的class最终都继承自Object,而Object,Object 定义了几个方法
toString() : 把instance输出为 String
equals(): 判断两个实例是否相等
hashCode:计算一个instance的哈希值
在必要的情况下覆写 Object 的这几个方法
public class Objectfuxie {
public static void main(String[] args) {
Object O = new Strkind();
System.out.println(O.toString());
System.out.println(O.equals(O));
System.out.println(O.hashCode());
}
}
class Strkind{
public String name = "xianghong";
@Override
public String toString() { //覆盖 Object中的 toString() 方法
return "Name: " + name;
}
@Override
public boolean equals(Object o){ //覆盖 Object 中的 equals() 方法
if (o instanceof Strkind) {
Strkind S = (Strkind) o;
return this.name.equals(S.name);
}
return false;
}
@Override
public int hashCode() { //覆盖 Object 中的 hashCode() 方法
return this.name.hashCode();
}
}
输出:
调用super
在子类的覆写方法中,如果调用父类的被覆写方法,需要通过 super 来调用
public class callsuper {
public static void main(String[] args) {
Fathers F = new Sons();
F.steOne("abc");
System.out.println(F.One()); //First abc!
}
}
class Fathers{
protected String onestr;
public void steOne(String onestr) {
this.onestr = onestr;
}
public String getOne() {
return this.onestr;
}
public String One() {
return "First " + getOne();
}
}
class Sons extends Fathers{
@Override
public String One() {
return super.One() + "!";
}
}
final
继承可以允许子类覆盖父类的方法,如果一个父类不允许子类对它的某个方法进行覆写,可以将该方法标记为 final;同理如果一个类不允许被继承那么 在类前 加一个 final 字段
不允许重写实例
class Father{
public final sonfun(){}
}
class Sonone extends Fathers{
@Override
public final sonfun(){} //报错不会允许被重写
}
不允许继承实例
final class Fathers{
...skip...
}
class Son extends Fathers{ //会报错
...skip...
}
注意: final修饰的field必须在创建对象时初始化,随后不可修改
三、抽象类
1、抽象类
由于多态存在,任意子类都可以重写父类的用一个方法,如果父类中一个被重写的方法没有意义,也不能去掉父类中被重写的方法,去掉的话失去多态的特性编译也会报错;如果父类的方法本身不需要实现任何功能。仅仅是为了定义方法签名,目的是让子类覆写他,那么只需要把父类的方法声明为抽象,声明的字段是; adstract
abstract class Fathers{
public abstract void fun();
}
把一个方法声明为 abstract ,表示为一个抽象的方法,本身没有实现任何方法的语句。因为是抽象的无法执行,所以 上面的Fathers 类无法被实例化,所以必须把 Fathres 类本身声明为 abstract 。才能正确编译;注意无法实例化抽象的类,抽象类本身被设计成只能用于被继承,所以抽象类可以强迫子类实现其定义的抽象方法,否则编译就会报错
面向抽象编程
public class abstractclass {
public static void main(String[] args) {
// Fathersss O = new Fathersss();
Fathersss F = new Son();
F.run();
Sontwos S = new Sontwos();
S.run();
}
}
abstract class Fathersss{
public abstract void run();
}
class Son extends Fathersss{
@Override
public void run() {
System.out.println("Son.run");
}
}
class Sontwos extends Fathersss{
@Override
public void run() {
System.out.println("Sontwos.run");
}
}
输出:
四、接口
1、接口
在抽象类中本质上是定义接口规范:就是规定高层的接口,从而保证所有子类都有相同的接口,这样,多态就可以发挥其作用
如果一个抽象类没有字段,所有方法全部都是抽象类方法,就可以把该抽象类改写为接口:interface
在java中可以使用 interface 声明一个接口
interface Father{
viod run();
String getName();
}
interface 就是比抽象类还要抽象的纯抽象接口,其中字段都不能有,接口定义的所有方法默认对都是 public abstract 的,所以这连个修饰符不需要写出来。
一个具体的 class 去实现一个interface 时,需要使用 implements 关键字。
interface的表示方式:
//声明了一个接口,这里的class不写
interface Fatherss{
void run();
}
public class implement {
public static void main(String[] args) {
Fatherss F = new Sonss();
F.run();
Sonss S = new Sonss();
S.world();
S.run();
}
}
interface Fatherss{
void run();
}
interface Hello{
void world();
}
class Sonss implements Fatherss,Hello{ //这里是一个类可以实现多个 interface
@Override
public void run() {
System.out.println("OK!");
}
@Override
public void world() {
System.out.println("Hello World");
}
}
输出:
这里是一个类可以实现多个 interface
2、接口继承(接口:interface)
一个 interface 可以继承自另一个 interface ,interface 继承自 interface 使用 extends,相当于拓展了接口方法
public class interfacess {
public static void main(String[] args){
C F= new C();
F.setCstr("B");
F.astr(); //A
System.out.println(F.bstr()); //B
}
}
interface A{
void astr();
}
interface B extends A{ //interface B 继承了 interface A
void astr();
String bstr();
}
class C implements B{
private String bstr;
@Override
public void astr() {
System.out.println("A");
}
public void setCstr(String bstr) {
this.bstr = bstr;
}
@Override
public String bstr() {
return this.bstr;
}
}
输出:
default 方法
接口中可以定义 default 方法
public class defaultmethod {
public static void main(String[] args) {
Sone F = new Sone();
F.setName("Xiaohong");
F.run();
}
}
interface Fatherssss {
String getName();
default void run() { //将 run() 方法改为 default 方法
System.out.println("Go! " + getName()); //Go! Xiaohong
}
}
class Sone implements Fatherssss{
private String name;
public void setName(String name) {
this.name = name;
}
public String getName() {
return this.name;
五、静态字段和静态方法
1、静态字段和静态方法
在一个class 中定义的字段,我们称之为实例字段,其特点是每个实例都有独立的字段,各个实例的同名字段互不影响;还有一种字段,使用 static 修饰的字段,这个就是静态字段; static 字段;
package classs;
public class statics {
public static void main(String[] args) {
Personss P = new Personss("Xiaohong ",13);
//Personss P1 = new Personss("Xiaoming ",13);
Personss.number = 123;
System.out.println(P.name + P.age);
System.out.println(Personss.number); //123
Personss.number = 321;
System.out.println(Personss.number);
}
}
class Personss{
public String name;
public int age;
public static int number; //定义一个static修饰的静态字段
public Personss(String name,int age) {
this.name = name;
this.age = age;
}
public String getName() {
return this.name;
}
public int getAge() {
return this.age;
}
}
输出:
静态方法
有静态字段就有静态方法,用 static 修饰的方法就是静态方法,调用实例方法必须通过一个是实例变量,而调用静态方法则不需要实例变量,通过类名就可以调用,静态方法类似其他编程语言的函数。
public class staticmothod {
public static void main(String[] args) {
Staticfun.setNumber(99); //直接调用,没有进行实例化
System.out.println(Staticfun.number); //99
}
}
class Staticfun{
public static int number; //定义静态字段
public static void setNumber(int value) { //定义静态方法
number = value;
}
}
因为静态方法属于 class 而不属于实例,因此,静态方法内部无法访问 this 变量,也无法访问是实例字段,只能方法静态字段,通过实例变脸也可以调用静态方法,但这只是编译器自动把实例改写成类名而已;如果太难过实例变量访问字段和静态方法,会有编译警告
接口静态字段
因为 interface 是一个纯抽象类,所以它能定义实例字段,但是,interface 是可以有静态字段的,并且静态字段必须是 final 类型;那么实际上,因为 interface 字段只能是 public static final 类型,所以我们可以把这修饰符去掉
未去掉时:
public interface Father{
public static final int numberone = 1;
public static final int numbertwo = 2;
}
可以简化为:
public interface Father{
//编译器会自动加上:public static final
int numberone = 1;
int numbertwo = 2;
}
六、包/作用域
1、包
java中定义一个空间,就为包:package 。一个类总是属于某个包,真正完整的类名是 :包名.类名
在定义 class 的时候,我们需要在第一行声明这个 类 属于哪个包
package packageName; //申明包名
public class classname{
...
}
在java虚拟机执行的时候,jvm只看完整的包名,所以包不同所以类就不同。包可以是多层次结构,,用 . 隔开,包是没有父子关系的
包的作用域
位于同一个包的类,可以访问报的作用域的字段和方法,在不用publc、protected、private 修饰的字段和方法就是包的作用域
import
在一个 class(类)中,我们总会引用其他 class(类),引用其他类的方法:
第一种就是直接写出完整的类名:PackageName.ClassName,这写起来繁琐;
第二种就是用 import 语句,导入完整的类名:import PackageName.ClassName;再使用时可以直接 写ClassName,在写import 的时候可以使用 * ,表示把这个报下的所有 class(类)都导入进来,但一般不推荐
第三种就是 import static 的语法,他可以导入一个类的静态字段和静态方法(很少用)
如:
package classs;
import static java.lang.System.*; // 导入System 类的所有字段和方法
public class importstatic {
public static void main(String[] args) {
out.println("123"); //相当于调用System.out.println(..)
}
}
输出:
注意:
1、为了避免名字冲突,需要确定唯一的包名,推荐做法就是使用倒置的域名来确保唯一性。如:com.packagename,only
的形式
2、JDK的核心类使用 java.lang 包,编译器会自动导入,(所以直接使用了 System.out.println()…)
2、作用域
在java中,常见的 public、protected、private 修饰符,这些修饰符可以用来限定方位作用域
public
定义为 public 的 class、interface 可以被其他任何类访问
private
定义为 private 的 字段、方法无法被其他类访问,所以private访问权限被限定再class 的内部的,而且与方法声明的顺序无关。推荐把 private 方法放在后面,因为java中支持嵌套类,如果一个类内部还定义了嵌套类,那么,嵌套类拥有访问 private 的权限
protected
protected 作用域继承关系,定义的 protected 的字段和方法可以被子类访问,以及子类的子类
局部变量
在方法内部定义的变量成为局部变量,局部变量作用域从变量声明处开始到对应的块结束,方法参数也是局部变量
final
用 final 修饰的 class 可以阻止被继承,修饰的方法也不用被子类覆写,修饰的字段也不能被重新赋值,用final 修饰的局部变量可以阻止被重新赋值