' i; o$ w: \) G) e# K W' f# g$ N" s1 h9 I) }0 i
前言7 l2 [% U, C" V, u" X" A
" V3 o( _; @/ n/ V& f- [; A
社会中就有继承的概念(百度百科):继承是指一个对象直接使用另一对象的属性和方法。也指按照法律或遵照遗嘱接受死者的财产、职务、头衔、地位等。这个解释中的前一句正说明了我们要今天要讲的对象(继承)的大体特性。0 E& f/ l( g; U5 }3 |! Q
+ e5 N7 V2 e9 Q. T9 [4 S2 ~) J2 H# ?% D/ e. B
' [* z; Z8 D: [* b+ \4 G- e l4 g
在写Java时用到继承的情况有?& k4 k9 U/ t8 }5 }
7 C7 y2 Q( ]& z& a" a当我们准备编写一个类时,发现某个类已有我们所需要的成员变量和方法,假如我们想复用这个类的成员变量和方法,即在所编写类中不用声明成员变量就相当于有了这个成员变量,不用定义方法就相当于有了这个方法,那么我们可以将编写的类声明为这个类的子类即继承。# k q' j9 H8 J
+ \9 b& U* d# u6 k$ q
小知识: : [% U* s4 Z/ P- C6 P9 k3 R: B- ~0 D0 ]$ O) O
源类,基类,超类或者父类都是一个概念# @) r' @, j7 O2 P4 Q1 v5 |) w
3 k& c/ Q- [% M2 x. A1 Q! P3 _' L
导出类,继承类,子类也都是同一个概念: V1 }# v+ x+ n& e9 l; h
$ Y0 w( E2 p, G, ?+ ?9 F1 a. |
继承的语法# ^7 r4 M, T& |1 w$ Y$ N2 m
# j K5 J7 c/ Z; k- t) E* K6 G
在类声明中,使用关键字extends来声明一个类的子类:3 g1 c7 [/ {2 o3 j8 L
7 W' s6 A/ Z/ Z1 p$ ~$ o
class 子类名 extends 父类名 { ' a2 b& t& y! }! k4 l 8 W/ @3 K; _& t# ?7 @( Z) x8 B … 9 S0 h8 y; ~' n' H8 B @% ~7 d6 I I" D/ e0 U" w
}) G, w) }6 h" \' e9 |7 r
/ v6 F5 w* k. A0 W* n* E0 b) ] 注意:如类声明语句中没有extends子句,则该类为java.lang包中的Object的子类。这就说明了java中的代码其实都有一个继承的关系,只不过是继承Object这个java中最根本的父类。( u# @1 R4 f2 b
9 l' `0 N/ f; ] V1 s% s$ F% Q继承的特点; y; H% r1 q" F' K* M+ T- `- Y
0 S3 K7 }3 l( i6 M; f# f子类拥有父类非private的属性和方法,子类继承的父类方法和成员变量可以当作自己的方法和成员变量一样被子类的实例方法使用。$ h' h0 K6 o+ B: U
子类可以有自己属性和方法,即子类可以在父类的基础上对父类进行扩展。 1 k0 [3 a R' z v& l子类可以用自己的方式实现父类的方法。(重写或者覆盖) " @7 X* X) Z( z7 H% k! s
访问权限和继承之间的关系 ' A% K9 B$ @/ V" r4 F$ [8 [& [& N% D5 g! D) `
访问限制修饰符限制对象对成员变量操作和方法调用,也限制继承性 3 Y5 ~6 X( w) O+ B. O! h8 q& g% s0 l/ q6 ?6 M
当子类和父类在同一个包:父类private成员变量和方法不会被子类继承9 R3 R; x9 G, u! a* K$ W& w4 L
当子类和父类不在同一个包:父类private和friend(默认)成员变量和方法不会被子类继承 $ s3 N7 E2 @6 y, p$ r2 I" h* @
$ m c% D9 ~2 A- \, B, P( c ( ^% x* B( I6 w构造器对继承的影响4 K: _) j! G5 K8 t9 D
+ P: r2 H2 n) R
什么是构造器: 9 [, _: o8 o8 b9 }2 T4 U7 p% W" H% Y7 M6 z6 f! S
构造器,也称构造方法、构造函数。作用是构造出来一个类的实例,确保对象得到初始化。, j* a$ e3 {& `# H5 `9 {, z
构造器的格式: 权限修饰符 类名(无参/有参){}。) k9 h3 n7 v& z# x; B
根据有无参数,可分为无参构造 和有参构造。 0 T; F( o; D2 s$ {构造器的特性: ( I! o; L8 X8 e) G 9 v6 ~. q6 f8 z( g+ L与一般方法名不同的是,构造方法名必须和类名保持一致,并且没有返回值。1 ~3 S, j, z+ z' m* d" I
Java编译器会自动创建无参构造函数,因此在类中,无参构造即使没有,我们也可省略不写。实例化对象时无需赋值。! j ^9 p2 x& Q% O5 M! ?* q3 c
倘若类中已存在有参构造函数,则编译器不再提供默认无参构造。实例化对象时需赋值,不然报错。, p. p1 T& } m1 p
当类实例化一个对象时会自动调用构造方法。 * j; N' t, r' ~* [$ j8 t6 H* G. U3 F构造器在Java的继承中会产生什么影响呢? 5 j: }7 t+ _7 n! C) G7 ~ $ m) d# ]: v9 l, M3 B: ~我们虽然继承了父类但是父类的构造器是我们子类继承不了的,我们只能去调用父类的构造器。那么在继承并且编写子类的构造方法的时候就要注意两种情况了:1.父类没有重写了构造函数(即构造函数是Java默认给的一个构造函数);2.父类重写了构造函数。 2 u0 B2 A& T; h$ q
6 g/ S- x9 O( k/ J- x( q/ }4 c父类使用默认构造器5 _! z- X7 \7 A: E9 R
% h) T$ d1 D- b
package Extends;. _2 X/ Y1 q. R' |1 W% l
% D, Z2 K8 X/ y# I1 D
public class Father { ^; |- s, a2 w& _% H Father(){ //默认的看不出区别所以这边设置个无参构造器效果一样的6 n4 @) u! C0 I4 F5 }
//当你不定义的时候就是这个构造器+ z; L9 q8 P. O+ X- t( p# [
System.out.println("This is Father Constrctor");5 [ P" _8 }' b
}$ Z% O( p. T$ c5 }6 b+ u
}" K- H1 }. n7 O j
package Extends;9 F4 p9 L6 b& k: Y6 Y* B9 n2 `
& ]- U+ z: D' Hpublic class Nosuper extends Father{. c, g8 P4 q5 U0 A% [7 x5 o1 f L
Nosuper(){ //定义子类的构造器 & `; Y! v( E H, q5 |4 \ System.out.println("This is Nosuper Constrctor"); * [+ U! f# Y5 S0 C5 b } , A5 H6 f4 U# I% T! g7 B7 F+ T( Z" t3 }* Z9 n6 e4 s
public static void main(String[] args) {" Z/ r% C1 e- s; g6 a: U# O
// TODO Auto-generated method stub) ?6 i2 h* L9 z6 N) n
Nosuper no=new Nosuper(); 5 [- Z% }7 w+ ?3 k) ^ }2 m+ R2 K" Q/ | v) ?$ R
/ Z7 G% s7 p# n/ ?# j$ c Y3 C- F}% z6 L: F( U2 | P
OUTPUT: & z6 e- i4 s9 h+ [7 y, q" LThis is Father Constrctor 4 `% H+ E3 e' P' D0 ^/ lThis is Nosuper Constrctor. h! [% Z0 I5 l: V( R
父类不使用默认构造器 # P4 g, u$ t! Q+ z k% b2 M N % D f. f4 [ v% \. P7 g; npackage Extends;) C3 H# T8 K0 y5 f9 k I
! I' N. T( P" d* d* t+ Zpublic class Father {/ ~8 f. |" {$ l! N) {2 |
Father(String name){. \7 {) G3 V7 x8 @: k9 m, Y
System.out.println("This is Father Constrctor,name is "+name); a% K3 S; z6 M, ?) Q* s+ e9 e } . C1 O9 C+ g: Y} ! J) ~6 C) {; p7 d, H, h9 B5 Vpackage Extends; ! V9 p, g0 [7 o3 ?) C0 Z) p0 f) ]% o7 R) Y" H$ c6 D
public class Nosuper extends Father{ $ c/ _5 z+ `$ }# s. M( s2 R; v Nosuper(){ 2 b1 d( y( e/ [; y super("YYH"); //必须先调用父类的构造函数否则会报错 # M" `# u* {& v4 u) Y System.out.println("This is Nosuper Constrctor");( Y5 a+ F' s+ a/ @" c
}" V( `% U, | I# T7 T$ P
public static void main(String[] args) {* u6 \; k9 |$ T2 |
// TODO Auto-generated method stub * z$ B4 g6 V; L* m9 r5 g Nosuper no=new Nosuper();5 A9 g6 R% k1 _! u; R& e- P( U0 q) _3 }
} W5 G+ d6 V) o! ^/ _- _4 N1 w} 1 O P# k1 C5 w5 @0 bOUTPUT: ' t- i% l- y. F# M& Q1 `8 `. a& LThis is Father Constrctor,name is YYH/ j3 X/ P1 w5 R, J% a X
This is Nosuper Constrctor " Z- k3 _# K3 ~& ^7 }) D+ ^3 s总结:对于继承来说,子类会默认调用父类的构造器,但是如果没有默认的父类构造器,子类必须要调用父类的构造器,而且必须是在子类构造器中做的第一件事(第一行代码)。2 q2 H. c7 u: H& O8 z% Z2 o5 w
4 N1 a! u! e9 o. s2 h 7 m! I, Q4 m2 `/ t, f9 Q6 [ + L# j( {# e) q- z" C; X$ z继承中的向上转型9 j4 x' c$ w+ [4 D" P9 t
: c3 r& f8 O4 E# Q2 ^当超类对象引用变量引用子类对象时,被引用对象的类型而不是引用变量的类型决定了调用谁的成员方法,但是这个被调用的方法必须是在超类中定义过的,也就是说被子类覆盖的方法,但是它仍然要根据继承链中方法调用的优先级来确认方法,该优先级为:this.show(O)、super.show(O)、this.show((super)O)、super.show((super)O)。 2 q2 l; M" i( Y0 X* V J: @9 f- Z s$ Q5 p; X
package _4_8;: t9 a8 ~9 ?& s3 d1 b
0 x, e* F& B1 ?$ A
class A { 5 W+ z6 s! m1 A public String show(D obj) { $ w4 y$ v1 {6 c! ]" J& E3 }% K return ("A and D");8 b/ E5 ~' H# x. s* g7 }$ p/ W! F
} ' G( P1 s5 B! a" X& @4 y5 ] public String show(A obj) { 0 A! p; a6 n+ j% ~8 D/ Q2 r return ("A and A");( j2 R- n# I& r1 K A+ W, G
} 2 R. l$ r2 s2 w# J5 {3 N
" r* _( N1 v& Y$ P4 ]4 f6 y$ D}9 R/ A, L. g: e: q1 K5 k) ?+ D
/ S) X0 T4 \( I
- _0 K: |" }1 w
class B extends A{ 7 M* N% v+ F( m2 N2 d' r6 v+ { //重载: B2 ?: T) Y& W' g& \/ k c
/ S6 I% E; Q: [$ \2 F( D
public String show(B obj){ & X8 I/ w h# i5 O1 T0 A return ("B and B");/ ?5 i. f/ `) K2 s/ K8 @, C
}) _8 ^0 I- |. @$ ]) z
//重写 - I* X" s6 e2 [" F+ S! ?* v public String show(A obj){ 5 {8 X1 }, m+ l- ~2 f2 |4 G! Z return ("B and A"); 3 g# i. q6 S4 A+ Q$ d# ~ } ( r5 B+ ^1 b1 u! [ ; z) M& E2 L/ A6 D# `- S9 v4 w} ; w9 J# T5 k4 b9 w3 M# m/ c, Q4 | 9 k0 w( F0 ?1 o! H" E ) {; D, P# c y) D* J& v% @class C extends B{} ( Z6 f9 i5 X1 |2 j' hclass D extends B{} ) A9 U, w8 y+ B5 spublic class t {$ R( C' e T$ [5 Z6 y9 _% ~
public static void main(String[] args) { + o" u( w" N: z! c7 {( | A a1 = new A();! R' A3 k! W t6 ^* p, D
A a2 = new B(); //1.只能调用子类中重写父类的方法,不可以 - q% [0 [2 ]# `* ], x
//调用重载的方法( q/ Q; ?# y# k u/ H' m/ ~9 B
B b = new B();- A9 F, f( k/ H: \ S+ S! N( P
C c = new C();/ l! ]2 n- X6 e7 C3 l
D d = new D();8 F" @- s- W U; c4 r e
System.out.println("1--" + a1.show(b));6 G5 \! \3 ?) Z# T$ u
System.out.println("2--" + a1.show(c)); # i& m! m( t: K System.out.println("3--" + a1.show(d));" H# j8 W. H8 g9 T0 v! Y5 P
System.out.println("--------");/ |+ D- F2 K+ W ^
System.out.println("4--" + a2.show(b));, s" R- p4 P1 r% @
System.out.println("5--" + a2.show(c)); [0 e" w- f# m- T2 t+ v) d; ~3 u
System.out.println("6--" + a2.show(d));: `9 h' \0 R, H8 ~- T
System.out.println("6--" + a2.show(a1));! u$ l" s# l5 K/ N- R
System.out.println("*"); 4 A' a! Z8 t- h, t# S8 M System.out.println("7--" + b.show(b)); 6 d2 l6 l: N; H System.out.println("8--" + b.show(c));1 ~# K- i4 s+ F) j1 s' Z- u2 Y
System.out.println("9--" + b.show(d)); 8 @; G2 {- O6 ]- f8 o } ' g v' C' z( d! a& Z4 s/ p} ; ?# H* W) f9 s3 O: g2 [OUTPUT: 8 b4 K7 V! z/ r/ T1--A and A. \* N$ b3 E" C- z! I+ E
2--A and A 4 _/ f# S+ |& o! A9 G$ ]* t3--A and D1 Y) E: y* ?- l$ H
--------) g) ^1 W' e! X* M) E1 Z& i S
4--B and A1 [4 B9 `; W. y) U' k3 U
5--B and A- J/ t, e! X& q4 S6 n7 J1 g1 M
6--A and D! c4 E6 l( E4 G$ y
6--B and A( O6 s* o; D+ r$ s; d, \) C
* 9 h& Z: h: E2 g5 _: J7--B and B& Y& s, U8 {$ P# B* F# x. i
8--B and B" u& x! x6 G7 m5 }, g" v( `: K9 K
9--A and D6 g- S) |& A% R c
仔细理解一下其中的代码不难理解其中向上转型在继承中的作用。按照this.show(O)、super.show(O)、this.show((super)O)、super.show((super)O)这个顺序一步一步的执行就会得到结果。 W0 p+ u6 ~% Y( p0 Z
1 e/ Q: b! {( {! V4 U" l! g6 p! K
将子类转换成父类,在继承关系上面是向上移动的,所以一般称之为向上转型。由于向上转型是从一个叫专用类型向较通用类型转换,所以它总是安全的,唯一发生变化的可能就是属性和方法的丢失。 9 r, c/ q; H1 Q+ _' Q0 e, z$ a : c1 K( B3 ^8 [, M5 W9 f继承慎用 3 C. o- b1 p: g l7 O0 `; b7 S: M! O% M7 k2 C+ T& [3 T; }
你会感觉当你在编写三四个程序的时候用继承解决了他们公共的部分是很方便的,但是如果你程序很大,class很多而且都继承于一个类中,当你想通过改变父类的一些信息来改变一个子类的信息的时候就会发现其他的子类也会随之改变,有时候会对其他的子类产生不必要的结果。 1 k1 x: L+ b: x# B" P" {: O6 f3 G1 [! o) H- n. W$ _
父类变了其他的子类就会随之全部改变。* F0 v3 \0 Q* [# p# q& ]7 [. K& B
3 Q$ y, e' Z$ m' }
继承破坏了封装,对于父类而言,它的实现细节对与子类来说都是透明的。! @1 D# C; V" [& B: u
7 V+ B" t" b! o% C* v8 n1 k' B* M
继承是一种强耦合关系。9 A% E2 \5 D. Q5 T8 z2 ^
7 `# n! C# j: u! |) W
“问一问自己是否需要从子类向父类进行向上转型。如果必须向上转型,则继承是必要的,但是如果不需要,则应当好好考虑自己是否需要继承”——《Think in Java》 + s) [/ k$ ?. F# c2 ~# `
' w; s3 k* K$ D这些都是自己翻阅资料或者是查看其他大佬的blog总结的,如有侵权请告知!!!如有错误请指出,谢谢。 - v/ W6 A' {' E, q # q/ l" l3 I. d4 B. K) D希望对你们有所帮助,本人也正在学习Java的路上也是小白一枚,还需要大佬们的多多支持。8 s' [1 E. K# ^9 J