Java SE 是什么,包括哪些内容(十八)?

本文内容参考自Java8标准
再次感谢Java编程思想对本文的启发!

开篇一句很重要的话:在面向对象的程序设计语言中,多态是继数据抽象和继承之后的第三种基本特征

Java面向对象程序设计的三大要素:封装,继承,多态(顺序不能乱)
⑴、封装:对应数据抽象,如何将现实中的事物抽象成一种数据类型(用代码表述)
"封装"通过合并特征(类变量)和行为(方法)来创建新的数据类型。其中"实现隐藏"则通过将细节"私有化"把接口和实现分离开来(这种分离的好处重点不在于应用,而是方便代码的维护)。
⑵、继承:对应类的继承extends、接口的实现implements(接口本质上也是一种类)
继承允许将对象视为它自己本身的类型或其父类型来加以处理。这种能力极为重要,因为它允许将多种类型(从同一个父类导出)视为同一类型来处理,而同一份代码也可以毫无差别地运行在这些不同类型之上了(框架代码)。
⑶、多态:对应多态
多态的作用则是消除类型之间的耦合关系(比如,框架代码只与餐具类型耦合,而不会与具体的餐叉、勺子、小刀耦合),它必须建立在继承关系之上。
"多态方法调用允许一种类型表现出与其它相似类型之间的区别,只要它们都是从同一父类导出而来的,这种区别是根据方法行为的不同而表现出来的,虽然这些方法都可以通过同一个父类来调用。"这几句话是Java编程思想中文第四版原书的翻译,个人觉得翻译的实在是有点蹩脚(可能翻译的人本身不是很懂吧),实际上这几句话要表达的意思是:多态允许同一个父类/父接口的方法在不同的子类中有不同的实现,根据你使用的子类不同,多态将表现出不同的行为(因为每个子类对这个方法的实现都不同)
只有将封装继承完全弄明白了,才能领悟到多态带来的好处。
多态通过分离做什么和怎么做,从另一个角度将接口和实现分离开来。
多态不但能够改善代码的组织结构和可读性,还能够创建可扩展的程序—即无论项目在最初创建时还是在需要添加新功能时都可以"生长"的程序
多态也称为"动态绑定"、“后期绑定”、"运行时绑定"
在前一篇博文中,我已经提到:对象既可以作为它自己本身的类型使用,也可以作为它的父类型(或根类型)使用,这种把某个对象的引用视为其父类型的引用的做法被称作向上转型(因为在继承树的画法中,父类是放置在上方的)
下面通过一个有关乐器的代码例子说明(因为乐器都要演奏乐符,所以单独创建一个乐符类):
代码示例:

// 向上转型//枚举类Note,代表乐符public enum Note{//包括了三种乐符MIDDLE_C,C_SHARP,B_FLAT;}//类Instrument,代表乐器类,但是不代表具体的乐器类型//它是所有乐器类的父类class Instrument{//方法play(),带一个Note类型的形式参数npublic void play(Note n){//打印字符串"Instrument.play()"System.out.println("Instrument.play()");}}//类Wind,代表具体的乐器(具体是什么乐器我百度了没查到),从父类Instrument继承class Wind extends Instrument{//重新实现了父类Instrument中的方法play()public void play(Note n){//打印字符串"Instrument.play()"System.out.println("Wind.play() "+n);}}//类Music,代表的是乐器的演奏public class Music{//方法tune(Instrument i),带一个Instrument类型的形式参数i//类似框架了,固定接受Instrument类型的参数(当然,我们已经知道,除了Instrument//类型本身,还接受它所有的子类对象)public static void tune(Instrument i){//调用Instrument类的方法play(Note n),传入了一个实际参数,//也就是一个乐符Note.MIDDLE_C。i.play(Note.MIDDLE_C);}//程序执行入口main方法public static void main(String[] args) {//创建Instrument类的子类Wind的对象.Wind flute = new Wind();//将对象引用传入方法tune(Instrument i)中tune(flute);}}

结果示例:

在上面的代码示例中,类Music的方法tune(Instrument i)接受一个类Instrument的引用(实际上,它还接受任何类Instrument子类对象的引用)。
在main()方法中,当将一个类Wind(类Instrument的子类)的对象引用传递到tune(Instrument i)方法时,虽然未经任何的转换,但是程序未报任何错误,也没有任何的警告,运行后还能得出满意的结果----这样做是允许的,因为Wind从Instrument继承而来,所以Instrument的接口(也就是方法play(Note n))必定存在于类Wind中。也就是说类Wind的对象也能调用方法play(Note n)(从Wind向上转型到Instrument可能会"缩小"接口,但不会比Instrument的全部接口更少)。

现在,我们发现,将子类对象传入了"框架"中,似乎"框架"代码故意忘记了它的具体类型(忘记它是具体的Wind类型,而是将它当成Instrument类型处理)。

上面代码示例中的Music.java看起来似乎有些奇怪,为什么它故意忘记对象(Wind对象)的类型呢?
实际上,在进行向上转型的时候,就会发生这种类似"忘记对象类型"的情况。
试想一下,如果让**tune(Instrument i)**方法直接接受一个Wind引用作为自己的参数(也就是将它的形式参数换成Wind i),似乎更加直观。但是这样引发的一个重要问题是:
如果那样做了,就需要为系统内Instrument的每种类型都编写一个新的tune()方法,如果现在加入Stringed(弦乐)和Brass(管乐)这两种Instrument(乐器):
那么,代码需要做出如下的改变:
代码示例:

// 增加几种类Instrument的子类//类Stringed继承类Instrument,代表具体的弦乐class Stringed extends Instrument{//重新实现了父类方法play(Note n)public void play(Note n){//打印字符串"Stringed.play()"System.out.println("Stringed.play() "+n);}}//类Brass继承类Instrument,代表具体的管乐class Brass extends Instrument{//重新实现了父类方法play(Note n)public void play(Note n){//打印字符串"Brass.play()"System.out.println("Brass.play() "+n);}}//类Music2,需要与类Music做对比public class Music2{//方法tune(Wind i),带一个Wind类型的形式参数i,此方法与具体的子类型Wind//高度耦合.调用此方法时,只能传入类Wind的对象。public static void tune(Wind i){//调用Wind类的方法play(Note n),传入了一个实际参数,//也就是一个乐符Note.MIDDLE_C。i.play(Note.MIDDLE_C);}//方法tune(Stringed i),带一个Stringed类型的形式参数i,此方法与具体的子//类型Stringed高度耦合.调用此方法时,只能传入类Stringed的对象。public static void tune(Stringed i){//调用Stringed类的方法play(Note n),传入了一个实际参数,//也就是一个乐符Note.MIDDLE_C。i.play(Note.MIDDLE_C);}//方法tune(Brass i),带一个Brass类型的形式参数i,此方法与具体的子类型Brass//高度耦合.调用此方法时,只能传入类Brass的对象。public static void tune(Brass i){//调用Brass类的方法play(Note n),传入了一个实际参数,//也就是一个乐符Note.MIDDLE_C。i.play(Note.MIDDLE_C);}//程序执行入口main方法public static void main(String[] args) {//创建类Wind的对象.Wind flute = new Wind();//将对象引用传入方法tune(Wind i)中tune(flute);//创建类Stringed的对象.Stringed violin = new Stringed();//将对象引用传入方法tune(Stringed i)中tune(violin);//创建类的对象.Brass frenchHorn = new Brass();//将对象引用传入方法tune(Brass i)中tune(frenchHorn);}//代码的运行结果实际上已经很清晰了,与特定的子类高度耦合,//各自调用各自对应类型的方法,得出的结果自然是各自类的结果。}

在上面的代码中,为每一种Instrument的子类都重载了一次方法tune()。
如果按照上面代码的方式,完全可以行得通,但是有一个主要的缺点:必须为每一个新Instrument子类编写特定类型的方法(有多少个子类就有多少个重载方法),这意味着在开始时就需要更多的编程,这也意味着如果以后想添加类似tune的新方法,或者添加Instrument的子类,仍然需要做大量的配套工作,此外,如果我们忘记重载某个方法。编译器并不会有任何错误提示信息返回,这样关于类型的整个处理过程就变得难以控制了。
所以,还是回到tune(Instrument i)方法的形式,我们只写这样一个简单方法,它仅接收父类作为参数,而不是那些特殊的子类(不与具体的子类耦合),也就是说,我们可以完全不管子类的存在以及子类的多少,编写的代码只与父类打交道。
以上的描述正是多态允许的。
再回到Music.java类中,运行程序后,我们便会发现,虽然tune(Instrument i)仅与父类Instrument 打交道,但是,产生输出的结果却是Wind.play()的结果,这无疑是我们所期望的结果,这又是为什么呢?
再观察一下tune(Instrument i)方法:

// 多态方法tune(Instrument i)public static void tune(Instrument i){i.play(Note.MIDDLE_C);}

它接受一个Instrument引用,那么在这种情况下,编译器怎样才能知道这个Instrument引用实际指向的是Wind对象还是Stringed对象还是Brass对象呢?
实际上,编译器无法得知,因为在编译阶段,编译器只能知道它仅是一个Instrument类型,真正得知这个Instrument引用实际指向的是Wind对象还是Stringed对象还是Brass对象的时机是在运行期
为了更深入理解这个问题,接下来将研究"绑定"这个话题。

方法调用绑定

将一个方法调用同一个方法主体关联起来被称作绑定,若在程序执行前进行绑定(由编译器和连接程序实现),叫做前期绑定,可能你们以前从来都没有听说过这个术语,因为它是面向过程的语言中不需要选择就默认的绑定方式(非常常见的绑定方式),例如,C语言只有一种方法调用,就是前期绑定
但是在上述的代码示例中,如果用前期绑定的概念去理解,就会让人产生迷惑,因为,方法只与父类打交道,当编译器只有一个Instrument引用时,它不可能知道去调用哪个子类的方法。
想解决编译期代码只与父类打交道,但是在运行期能根据具体的子类得出结果的问题,只能通过后期绑定
后期绑定的含义就是在运行时根据具体的子类型对象进行绑定,后期绑定也叫做动态绑定运行时绑定
如果一种语言想实现后期绑定,就必须具有某种机制,以便能在运行期准确判断对象的类型,从而调用恰当的方法(多态指的就是在正确的时机调用了恰当的方法)。也就是说,编译器一直不知道对象的类型,但是方法调用机制能找到正确的方法体,并加以调用。后期绑定机制随编程语言的不同而有所不同,但是只要略微想一下就知道,不管怎样都必须在对象中安置某种"类型信息"。
Java中除了static方法和final方法(private方法属于final方法)之外,其他所有的方法都是后期绑定,这意味着通常情况下,我们不必判定是否应该进行后期绑定,因为在Java中,它(后期绑定)是自动发生的
为什么要将某个方法声明为static呢?正如前一篇博文提到的一样,它可以防止其他人覆盖该方法。但更重要的一点是:这样做,可以有效地"关闭"动态绑定,或者说,告诉编译器,不需要对这个方法进行动态绑定。这样,编译器就可以为final方法调用生成更有效的代码。然而,在现在的技术环境里看来,这样做对提升程序的整体性能并没有什么显著的改观,所以,最好是根据设计来决定是否使用final(详情请参见有关我描述关键字final的博文),而不是出于试图提高性能的目的来使用final。
一旦知道Java中所有方法都是通过动态绑定实现多态这个事实之后。我们就可以编写只与父类打交道的程序代码了(因为在编译期不用知道类型,所以有父类型就好,运行期有后期绑定,可以识别正确的类型,实现多态),并且这些代码对所有的子类都可以正确运行。或者换一种说法,发消息给某个对象(实际上就是用父类搭建好发消息的框架),让该对象去判定应该做什么事情(实际上就是让运行期的子类对象来做事)。

下面通过具体的例子来说明

在面向对象程序设计中,有一个经典的例子就是"几何形状(Shape)",因为它很直观,所以经常用到。
在"几何形状"这个例子中,有一个父类Shape,以及多个子类:如Circle(圆)、Square(方形)、Triangle(三角形) 等。这个例子之所以好用,是因为我们可以说"圆是一种几何形状",这种说法也很容易被理解。下面通过继承图来展示他们之间的关系:

向上转型可以像下面这条语句这么简单:

// 向上转型语句//将子类型的对象赋值给父类型的引用Shape s = new Circle();

在以上的代码中,创建了一个Circle对象,并立即把它赋值给Shape,这样做看似错误(将一种类型赋值给另一种类型),但实际上是没有问题的,因为通过集成,Circle就是一种Shape。因此,编译器认可这条语句,也不会产生错误信息。
假设你调用一个父类方法(这个方法已经在子类中被覆盖):

// 调用一个方法//调用的这个方法在子类中已经被覆盖s.draw();

你可能认为,调用的是Shape的draw(),因为毕竟是一个Shape的引用,但是,实际上调用的是Circle的draw(),这正是因为后期绑定(多态)。
你可以将所有的代码都写出来,再通过运行结果去证实。
再来看下另外的一个例子:
代码示例:

// 多态的另一个例子//父类Shapeclass Shape{//父类规定了行为,没有具体的实现//draw是绘画public void draw(){}//erase是擦除public void erase(){}//也就是说Shape包含了两种基本的行为,一种是绘画//另一种是擦除}//类Circle继承类Shapeclass Circle extends Shape{//重新实现了父类中的方法draw()public void draw(){//打印字符串"Circle.draw()"System.out.println("Circle.draw()");}//重新实现了父类中的方法erase()public void erase(){//打印字符串"Circle.erase()"System.out.println("Circle.erase()");}}//类Square继承类Shapeclass Square extends Shape{//重新实现了父类中的方法draw()public void draw(){//打印字符串"Square.draw()"System.out.println("Square.draw()");}//重新实现了父类中的方法erase()public void erase(){//打印字符串"Square.erase()"System.out.println("Square.erase()");}}//类Triangle继承类Shapeclass Triangle extends Shape{//重新实现了父类中的方法draw()public void draw(){//打印字符串"Triangle.draw()"System.out.println("Triangle.draw()");}//重新实现了父类中的方法erase()public void erase(){//打印字符串"Triangle.erase()"System.out.println("Triangle.erase()");}}//类RandomShapeGenerator,代表了Shape子类的随机生成器//能随机生成Shape类的子类对象class RandomShapeGenerator{//创建Java中的随机类Random的对象,随机数因子为43private Random rand = new Random(43);//运行一次能随机返回一个类Shape的子类的对象。public Shape next(){//switch语句,选择因子为rand.nextInt(3)计算式的结果switch(rand.nextInt(3)){//switch语句的默认执行default://如果rand.nextInt(3)计算式的值为0,返回Circle对象。case 0:return new Circle();//如果rand.nextInt(3)计算式的值为1,返回Square对象。case 1:return new Square();//如果rand.nextInt(3)计算式的值为2,返回Triangle对象。case 2:return new Triangle();}}}//类Shapespublic class Shapes{//组合类RandomShapeGenerator,创建类RandomShapeGenerator//的对象private static RandomShapeGenerator gen = new RandomShapeGenerator();//程序执行入口main方法public static void main(String[] args) {//创建了一个Shape类型的数组,一共是9个空间,每个空间都存储了一个//Shape类型的引用。Shape[] s = new Shape[9];//for循环,将数组s的9个空间的引用全部指向具体的对象。for(int i = 0;i<s.length;i++){//调用类RandomShapeGenerator的next()方法。//每调用一次就生成一个类Shape的子类对象。//赋值给数组的引用。s[i] = gen.next();}//循环数组的对象for(Shape shp : s){//调用每个对象的draw()方法。//这里能体现出多态的引用。shp.draw();}}}

结果示例:

Shape父类为它的所有子类建立了一个公用的接口(这个接口指的是所有子类都应该有的方法,本例中指的是draw()和erase())—也就是说,所有的Shape类的子类都可以绘画(draw())和擦除(erase()),子类再通过覆盖这些方法,给它们新的实现,从而为每种特殊的几何形状提供符合它们性质的行为。
类RandomShapeGenerator是一种"工厂",在我们每次调用next()方法时,它可以为随机选择的Shape引用关联一个实际的对象。
你需要注意的是,向上转型是在return语句里发生的。每个return语句取得一个指向某个Circle,Square、或者Triangle的引用,并将其以Shape类型从next()方法中发送出去。所以无论我们在什么时候调用nect()方法时,是绝对不可能知道具体类型到底是什么的(因为我们只能获得一个通用的Shape类型的引用,next()方法的返回类型是Shape类型)。
main()方法包含了一个Shape类型引用组成的数组,通过调用RandomShapeGenerator.next()来填入数据。此时,我们只知道自己拥有一些Shape,除此之外,你不会知道更具体的情况(实际上,编译器也不知道,它只知道Shape),然而,当我们遍历这个数组,并为每个数组元素调用draw()方法时,与类型有关的特定行为会神奇般地正确发生,我们可以从运行该程序时所产生的输出结果中发现这一点(结果中全是与特定子类有关的行为,并没有类Shape的draw()方法执行的痕迹)。
随机选择几何形状是为了让大家理解:在编译时,编译器不需要获得任何特殊信息就能进行正确的调用(因为随机选择,事先不知道任何特性类型的信息)。对draw()方法的所有调用都是通过动态绑定进行的。

可拓展性

现在,我们再返回到"乐器(Instrument)"示例,由于有多态机制,我们可以根据自己的需求对系统添加任意多的新类型,而不需要更改tune(Instrument i)方法。
在一个设计良好的OOP(面向对象设计)程序中,大多数或者所有方法都会遵循tune(Instrument i)的模型:只与父类接口通信
这样的程序是可拓展的,因为可以从通用的父类继承出新的数据类型,从而添加一些新功能。而tune(Instrument i)方法体(也就是框架代码)不需要任何改动就可以直接应用于新类(直接享受新类带来的新功能)。
那么,对于乐器(Instrument) 例子,如果我们在 父类(instrument) 中添加更多的方法,并加入一些新类,将会出现什么情况呢?
如图:

事实上,不需要改动tune(Instrument i)方法,所有的新类都能正确运行。
即使tune(Instrument i)方法是单独存放在某个文件中,并且在Instrument接口中添加了其他的新方法,tune(Instrument i)也不需要再编译就能正确运行。
下面是具体的代码示例:

// 代码示例//父类Instrumentclass Instrument{//方法play(Note n),带一个Note类型的形式参数.void play(Note n){ //打印字符串"Instrument.play()"+nSystem.out.println("Instrument.play()"+n); }//方法what()String what(){ //返回字符串"Instrument"return "Instrument"; }//方法adjust()void adjust(){ //打印字符串"Adjusting Instrument"System.out.println("Adjusting Instrument"); }}//类Wind继承类Instrumentclass Wind extends Instrument{//方法play(Note n),带一个Note类型的形式参数.void play(Note n){ //打印字符串"Wind.play()"+nSystem.out.println("Wind.play()"+n); }//方法what()String what(){ //返回字符串"Wind"return "Wind"; }//方法adjust()void adjust(){ //打印字符串"Adjusting Wind"System.out.println("Adjusting Wind"); }}//类Percussion继承类Instrumentclass Percussion extends Instrument{//方法play(Note n),带一个Note类型的形式参数.void play(Note n){ //打印字符串"Wind.play()"+nSystem.out.println("Percussion.play()"+n); }//方法what()String what(){ //返回字符串"Percussion"return "Percussion";}//方法adjust()void adjust(){ //打印字符串"Adjusting Percussion"System.out.println("Adjusting Percussion"); }}//类Stringed继承类Instrumentclass Stringed extends Instrument{//方法play(Note n),带一个Note类型的形式参数.void play(Note n){ //打印字符串"Stringed.play()"+nSystem.out.println("Stringed.play()"+n); }//方法what()String what(){ //返回字符串"Stringed"return "Stringed"; }//方法adjust()void adjust(){ //打印字符串"Adjusting Stringed"System.out.println("Adjusting Stringed"); }}//类Brass继承类Wind,这里你需要注意了//类Brass继承的是类Instrument的子类,没有直接继承//类Instrument.算间接继承,但是它仍然是类Instrument的子类class Brass extends Wind{//方法play(Note n),带一个Note类型的形式参数.void play(Note n){ //打印字符串"Brass.play()"+nSystem.out.println("Brass.play()"+n); }//方法adjust()void adjust(){ //打印字符串"Adjusting Brass"System.out.println("Adjusting Brass"); }}//类Woodwind继承类Wind,这里你需要注意了//类Woodwind继承的是类Instrument的子类,没有直接继承//类Instrument.算间接继承,但是它仍然是类Instrument的子类class Woodwind extends Wind{//方法play(Note n),带一个Note类型的形式参数.void play(Note n){ //打印字符串"Woodwind.play()"+nSystem.out.println("Woodwind.play()"+n); }//方法what()String what(){ //返回字符串"Woodwind"return "Woodwind"; }}//类Music3public class Music3{//方法tune(Instrument i),带一个Instrument类型的形式参数ipublic static void tune(Instrument i){//...//调用方法play(Note n)i.play(Note.MIDDLE_C);}//方法tune(Instrument i),带一个Instrument[]数组类型的形式参数epublic static void tuneAll(Instrument[] e){//...//遍历数组中的对象for(Instrument i:e){//将每个对象都作为实际参数传入方法tune(Instrument i)中tune(i);}}//程序执行入口main方法public static void main(String[] args) {//创建Instrument类型的数组orchestraInstrument[] orchestra = {//Wind对象,是类Instrument的直接子类。//在这里自动完成了向上转型new Wind(),//Percussion对象,是类Instrument的直接子类。//在这里自动完成了向上转型new Percussion(),//Stringed对象,是类Instrument的直接子类。//在这里自动完成了向上转型new Stringed(),//Brass对象,是类Instrument的子类Wind的子类。//等于是类Instrument的间接子类//在这里自动完成了向上转型new Brass(),//Woodwind对象,是类Instrument的子类Wind的子类。//等于是类Instrument的间接子类//在这里自动完成了向上转型new Woodwind()//虽然以上对象中既有直接子类,又有间接子类,//但是都是Instrument类型,都可以放入Instrument类型的数组中};//调用方法tuneAll(Instrument[] e),将数组orchestra//作为实际参数传入。tuneAll(orchestra);}}

结果示例:

新添加的方法what()返回一个带有类描述的String引用,另一个新添加的方法adjust()则提供每种乐器的调音方法。
在main()中,当我们将对象的引用(类Instrument直接子类和间接子类的对象引用)置入orchestra数组中,它们都会自动向上转型成Instrument。
从示例代码的执行结果中可以看到,tune(Instrument i)方法完全可以忽略它周围代码所发生的变化,依旧正常运行,这正是我们期望的多态所具有的特性。
我们所做的代码修改,不会对程序中其他不应该受到影响的部分产生破坏。换句话说,多态是一项让程序员"将改变的事物与未变的事物分离开来"的重要技术。
PS:时间有限,有关Java SE的内容会持续更新!今天就先写这么多,如果有疑问或者有兴趣,可以加QQ:2649160693,并注明CSDN,我会就博文中有疑义的问题做出解答。同时希望博文中不正确的地方各位加以指正。

JavaSE基础知识(十八)--Java多态之向上转型(多态初步)相关推荐

  1. JavaSE基础二十:Java 多线程(线程基础知识、Java 多线程、Java 实现多线程(继承 Thread 类、实现 Runnable 接口、实现 Callable 接口))

    本章目录 1.基础知识准备 2.Java 多线程概述 3.Java 实现多线程 3.1.继承 Thread 类 如何开启新线程 Thread 类常用方法 多线程中的同步 Thread 类同步方法 多线 ...

  2. 物流基础知识(十八)

    流通加工的概念 一.流通加工的概念 流通加工是流通中的一种特殊形式. 商品流通是以货币为媒介的商品交换,它的重要职能是将生产及消费(或再生产)联系起来,起"桥梁和纽带"作用,完成商 ...

  3. 【JavaSE 基础知识盲点小结】

    [JavaSE 基础知识盲点小结] 一.第一天: 1. 在 cmd 命令窗口问题: ①编译是: javac HelloWorld.java ②运行时是: java HelloWorld (类名 不加 ...

  4. 【Java基础知识 1】Java入门级概述,让阿里架构师告诉你为什么要分库分表

    1998年12月8日,第二代Java平台的企业版J2EE发布. 1999年4月27日,HotSpot虚拟机发布. 2005年6月,在Java One大会上,Sun公司发布了Java SE 6.此时,J ...

  5. java基础知识之初识java

    java基础知识之初识java JAVA基础课后总结 一 1.计算机程序 定义:程序(Program)是为实现特定目标或解决特定问题而用计算机语言编写的命令序列的集合. 2.指令 定义:指令就是指示机 ...

  6. JavaSE基础知识(五)--面向对象代码实现初步(实现一个简单的类类型代码)

    Java SE 是什么,包括哪些内容(五)? 本文内容参考自Java8标准 一.面向对象(代码实现): 首先,在这里我需要说明一个根本性的问题:实际上,面向对象编程包括了两部分,一个是你的编程思想,一 ...

  7. 面向对象-----6(javaSE基础知识完结篇)

    目录 解析面向对象 内存分析 三大特性.封装.继承.多态 接口 内部类 涉及知识目录总览: 资料来自于马士兵java笔记 这是javaSE基础知识的最后一篇笔记 1. 解析面向对象 返回目录 [1]面 ...

  8. 【Java】【基础知识】【Java的基本使用】

    [Java][基础知识][Java的基本使用] 基于jdk8 仅个人理解,或有疏漏 基于 java疯狂讲义 第三版和第四版 java核心技术卷一 第十版和第十一版 廖雪峰java课程 一.基本数据与结 ...

  9. Java SE 基础(十)Java中的异常

    Java SE 基础(十)Java中的异常 什么是异常 异常的处理 异常类 throw和throws 自定义异常 什么是异常 Java 中处理错误的一种机制,Java 中的错误大致分为两类,一类是编译 ...

最新文章

  1. 十七、生产者消费者问题
  2. 专访黄勇:Java在未来的很长一段时间仍是主流
  3. Spring RSocket:基于服务注册发现的 RSocket 负载均衡
  4. Spring AOP编程-传统AOP开发切点表达式写法介绍
  5. 2.关于QT中的Dialog(模态窗口),文件选择器,颜色选择器,字体选择器,消息提示窗口
  6. Linux下crontab(自动重启)的格式备忘
  7. Period II(FZU-1901)
  8. 2010年软考 考试日期安排
  9. 系统集成方案(一).NET集成方案
  10. 2018的锅让2019来悲
  11. Unity线性空间UI制作方面总结
  12. ORAN C平面 Section Extension 7
  13. Ultra Light Waterproof Jacket 2014 Warm down Coats Cheap
  14. 基于MATLAB的人脸识别系统
  15. Python学习,python3中的bytes和str类型
  16. GIS开发学习推荐书目
  17. [转载]10大适合学英语的美剧 你看过几部_拔剑-浆糊的传说_新浪博客
  18. PyQt5表格控件QTableWidget
  19. 详细总结Linux中的火墙策略优化
  20. 越是看起来不起眼的小生意利润越大

热门文章

  1. 唐 库利超级计算机,第七卷 乖离性 百万亚瑟王_第二百五十二章 绝望中的希望...
  2. hypothesisTest
  3. Win系统 - 如何添加新用户,怎么添加管理员帐户?
  4. DoS、DDos以及DRDoS攻击手段和防范措施
  5. 【华人学者风采】陈积明 浙江大学
  6. 亮剑精神--亮码精神
  7. 7-6 打妖怪 (10 分)
  8. win10如果虚拟化服务器,win10虚拟化服务器配置
  9. 2022Android春招面试,实战分析
  10. 大学物理上册详细笔记_大学物理上册课堂笔记