java基础

java基础

一、介绍

(一)内容介绍、开发环境安装

一:总述以及基础要求
语言层面开发要首先掌握好;
掌握二进制、八进制、十六进制,懂的软件的安装;

二:简要自我介绍、讲课方法
以少为多,用啥讲啥

三:学习方法
三个要素:
a)一个好老师远胜一本死教材;节约大家大量的学习时间
b)不停的努力是根本,不断的学习和实践,多看优秀的人写的代码,自己动手多参与实际项目写大量代码;
c)举一反三。要积极自学,善用百度;自助
老师在课程中的作用:把重要的知识讲解到位,把整个知识体系串起来;
扶上马,送一程,赋予你独立前行的能力---------走多远,靠你自己,不是老师;

四:Java语言的发展历程
a)1990年年底,Sun公司 Green计划,计划开发智能家电控制系统 ;Oak语言;
b)94年,互联网,浏览器的兴起;Oak语言商标被注册,所以修改为Java语言
c)95年,Sun发布Java语言,还需要强大的类库供我们使用;
d)96年,Sun发布了JDK1.0:JRE【JVM】,JDK
e)97年,jdk1.1,98年,jdk1.2;Java在1998年被分成了三个版本:J2SE ,J2ME,J2EE
f)2002年,jdk1.4
g)2004年,jdk1.5,J2SE ,J2ME,J2EE三个版本在这一年改名了 Java SE ,Java ME,Java EE
Java SE 5.0 = jdk1.5
h)2006年,jdk1.6[Java SE 6.0]
i)2009年,Oracle公司收购了Sun公司,Java就变成了Oracle公司的了;
j)2011年 java se 7, 14年java se 8,17年 java se 9。
2017年公布了一个全新的版本计划,这个计划使java的发布频率变成了半年一个版本,大概每年3,9月份;
所以目前java se 12

五:Java程序的运行机制
c++,编译型语言,要编译【链接】—>可执行文件【独立运行】,c++语言,称呼为编译型语言;
Java:先编译[javac],后解释[java[JVM]],JVM里可能会带着JIT【即时编译/动态编译】编译器,能够在运行时把
经常用到的指令【热点代码】弄成机器码保存在内存中,进一步的提升执行效率,这种情况下这部分热点代码就属于被编译执行而不再是解释执行了;

总结:Java既不是纯粹的编译型语言,也不是纯粹的解释型语言,Java程序的执行,需要先编译,后解释两个步骤,而且有一部分常用代码还会被编译执行;

(5.1)一次编译,到处执行【跨平台】
一次编译,就是.java源码文件编译成.class字节码【平台无关性】;
跨平台的关键因素,在于JVM;.class字节码文件,就是可以到处运行的文件;

六:开发环境安装和设置
JDK,全称:Java SE Development Kit,即Java标准版开发包,是Oracle提供的一套用来开发Java应用程序的开发包。
它提供了编译、运行Java程序锁需要的各种工具和资源,包括Java编译器、Java运行时环境,以及常用的Java类库等等;

(6.1)JDK的安装
浏览:https://www.oracle.com
我们可以通过Java开发环境来编译和运行Java程序;

(6.2)java开发环境的安装
a)Eclipse,免费的;
b)Intellij IDEA:免费的社区版本,付费的终极版
https://www.jetbrains.com/idea/ JetBrains

(二)java程序的基本结构

一:public class Main
class:类
Java程序必须以类(class)的形式存在,类(class)是Java程序的最小程序单位;
一个java源代码文件里,必须要有一个class存在;而且,你写的各种代码行,必须都要包含在这个class里边,不能跑到class外边去独立存在;
class:简单理解,把一种抽象概念,把一组事务的公共特性提取出来/抽象出来,就可以构成一个类(class)

二:最简单的java程序
最简单的Java程序,可以只包含一个空类的定义;
编译:Build->Build Project ,成功
运行:Run->Run Main,失败

三:main方法
java解释器/jvm(java虚拟机),一般java解释器是jvm的一部分。
如果某个类要想被java解释器执行,那么这个类边必须要有一个main方法,而且整个程序的执行就是从main方法开执行,换句话说main()方法是整个程序执行的入口;
main前边有 public static void 来修饰; main方法后边(),()里边 String[]
public static void main(String[] args12) 是固定写法,大家硬记

一个.java源码文件里边,肯定都有一个class类;所以,大型项目有多个.java文件构成,肯定也就会有多个class【类】。
但是,对于整个项目来讲,只应该有 一个程序执行入口,换句话说,这个main方法,只应该包含在某一个类中,
其他类中,是不应该再有这个main方法的,其他的类,都属于被这个main方法直接 或者间接的调用;

四:源文件名与类名的关系
源文件的主文件名 与 类名相同
java源文件的文件名起名特点
a)扩展名.java
b)如果这个.java源码文件里边含有 一个public类型的类,那么这个源文件的主文件名,必须跟这个public类的类名相同。
当然,一个java源码文件中最多只能定义一个public类。
当然,如果你这个java源码文件中定义的类不是public类,那么你java文件的主文件名可以任意取;
c)一个java源码文件中只定义一个类,不同的类用不同的.java文件来定义;
给java源代码文件起名的时候,保持源代码文件的主文件名与你定义的类名相同;

把文件扩展名要显示出来
java严格区分大小写
范例中大写的Main[类名],而main是程序执行起点【入口】,完全是两个东西

五:整体回顾

六:用IDEA创建一个空工程后逐步完善
创建工程:File->new->Project
a)如果想运行一个java类,那么这个java类中必须要包含一个main方法;而且main写法固定: public static void main(String[] args)
b)main入口函数注意大小写

二、数据类型、运算符的介绍

(一)数据类型简介、常量和变量

一:注释
以//开头,注释单独的一行,从//开始整个到行末尾都是注释
//对于程序的编译和执行,这些注释行不起作用,注释只是让读源代码的人更明了某些代码行的含义;
//高难度项目代码中的注释行占到整个源代码的1/3;
多行注释 /* */

二:语句的分隔
Java中采用 分号 作为语句的分隔,因此,每个Java语句必须使用分号作为结尾[注意 一定是 英文的分号,而不是中文的分号【中文的分号很粗大】]

三:Java语言的数据类型
整型:126,小数:18.5,字符串:“I Love China”;

a:一个字符需要1个字节的存储空间

整型,浮点型,字符型,布尔型

四:常量和变量
常量:直接量,程序运行中,其值不能被改变的量就叫常量;
150,12.3
变量:在程序执行过程中,其值可以被改变的量就叫变量;变量要用 一个名来表示,叫做变量名,变量在内存中会占用一定的存储单元;
起变量名要有一定规则,
标识符:就好象一个人名一样(张三,李四),是由 字母、下划线、美元符号$ 中的任意一个 开头,后边可以跟任意数目[3、5,10个8个,不应该太多]的字母、数字、下划线和美元符$
注意几个问题:
a)Java区别大小写字母,所以abc跟Abc是完全不同的两个标识符;
b)标识符不能以数字开头
c)标识符不能包含空格以及非$之外的其他特殊符号比如@
d)标识符不能是Java中的关键字/保留字
e)标识符长度,一般10个8个,虽然没有明确规定,但不建议太长

变量名 其实就是个标识符;

变量名起名的建议:[见名知义很重要]
iMemberCount

五:变量的定义和使用
遵循:先定义,后使用,定义格式如下:
类型名 变量名 [=初值]

(二)整数类型、浮点类型

一:整数类型
给一个变量初值的时候,给的值千万不要超过这个变量能够保存的数字范围;
最常用的整数类型是int类型,占4字节;
对于一个整数,缺省情况下,这个整数是什么类型呢?
a)单纯说一个数字,就认为他是一个整形;
b)但是如果把这个数字直接给一个byte,short这种类型的变量,只要这个数字范围符合byte,short这种类型,那么这个数字就变成了byte,short这种类型;
c) long al = 4000000000; 报错,提示Integer number too large
我们需要告诉操作系统,我们这个常量数字是个long型【否则,系统就会把这个常量数字当成 一个整型】
我们需要在常量末尾增加一个L或者l字母【告诉Java我们这个常量数字是个long型】;

整型常量最常用的是十进制;比如八进制,十六进制;
a)八进制数据以0开头;
b)十六进制数据以0x或者0X开头;

二:浮点类型
float(单精度浮点类型),double(双精度浮点类型)

浮点数常量有两种表示方法:
a)十进制数表示形式;0.12; 3.14159
float fd1 = 0.12; 报错的主要原因,就是因为Java语言的浮点数字类型默认是double类型;
我们把一个十进制数表示的浮点型常量末尾增加一个f或者F,那么就表示这个浮点型常量 是float类型了;
我们把一个十进制数表示的浮点型常量末尾增加一个d或者D,那么就表示这个浮点型常量 是double类型了;其实没必要加,因为浮点数字类型默认是double类型

b)指数形式/科学计数法形式:很不直观,很坑爹;
1.68E2 等价于1.68 * 10的2次方 = 168 = 168.0
1.68E+2 ,这个+可以省略
1.68E-2 等价于1.68 * 10 -2次方 = 0.0168
168E-2 等价于 168 * 10 -2次方 = 1.68
只有浮点数才能用指数形式来表示;

(2.1)浮点类型变量的特性
1个字节8位,
double类型变量比float类型变量多占4个字节,double类型变量能够保存的数据范围比float范围大得多,并且精度高很多;
浮点数在内存中是以指数形式来存储的,所以大家能够看到,它能够存储的数据范围非常大;[有 一些位拿出来专门保存指数,其他一些位拿出来专门保存尾数]
这种保存浮点数的保存方式,很可能没有办法精确的保存一个浮点数字;
所谓double类型比float精度高——也就是double类型能够保存的有效数字位数多;

float类型浮点数一般提供7位有效数字(考虑到四舍五入等等,保守认为6位有效数),double类型浮点数提供15到16位有效数字(考虑到四舍五入等等,保守认为15位有效数),数值范围随系统机器而异

有效数字的概念:
12345.678
精度是1位有效数字,10000.0
精度是2位有效数字,12000.0
精度是3位有效数字,12300.0
。。。
精度是7位有效数字,12345.67X

0.1234
精度是1位有效数字,0.1XXXX
精度是2位有效数字,0.12XXX

(2.2)几个特殊的浮点数
正无穷大,负无穷大,非数 ——溢出以及出错有关
a)正无穷大:用一个正数除以0就可以得到 正无穷大,用POSITIVE_INFINITY表示
b)负无穷大:用一个负数除以0就可以得到 负无穷大,用NEGATIVE_INFINITY表示
c)非数:0.0除以0.0 或者对一个负数开方讲得到一个非数,用NaN来表示
强调:所有正无穷大数值都是相等的,所有负无穷大数值也都相等;NaN不与任何数值相等,包括他自己;

(三)字符类型、浮点类型、类型转换

一:字符类型
(1.1)字符类型
字符类型通常表示 单个 字符,字符型值必须用 单引号 括起来; ’
Java语言使用的是16位【2个字节】Unicode字符集【字符集:就是给字符编个号然后放到一起就构成了字符集】作为存储单个字符时的编码方式。
Unicode,可以认为是一个字符集,也可以说是一种编码方式,因为Unicode是16位的,能够表达的字符是6万多个【2的16次方】。
这6万多个 包括中文字符甚至是其他国家语言的字符
‘a’ ‘中’ ‘国’

转义字符【特殊字符】,是以 \开头的字符序列
说白了:一个字符,就对应着一个数字;

(1.2)字符串类型
字符串:一长串字符,最少是一个,最多不限,一大串 ,字符串是用双引号 括起来;
“a” “我爱中国” “abc”
在Java中表达字符串 用的是一个叫String的类来表达字符串;

二:布尔类型 boolean
说白了,这个是判断一些东西是真还是假的;布尔类型变量其中保存的值要么是 真【true】,要么是假【false】

三:基本类型之间的类型转换
(3.1)自动类型转换 :小瓶水往大瓶里到;
如果系统支持 把某种基本类型的值直接赋给另一种基本类型的变量,这种方式就叫做自动类型转换;
看图:图中左边的类型表示的数字范围 一定 小于右边类型表示的数字范围;
把long 往float转要小心,一不小心,就容易把有效位转丢;

(3.2)和字符串值的连接
把任何基本类型的值【常量】和字符串值【常量】 用加号 进行连接【顺序无所谓】 ,那么整个加完后的结果将自动变成字符串类型;
小技巧:如果希望把一个基本类型转成字符串类型,那么可以把这个基本类型连上一个空字符;

  • :数值型计算是表示加法,得到和值
  • : 字符串连接运算符,得到一个字符串值

(3.3)强制类型转换:大瓶水往小瓶里到;
把图中右侧的类型往左侧转,那么就需要用到强制类型转换
(short)doule变量名;
格式:(要转换的目标类型名)要转换的变量或者值
但是一定要保证值不要溢出;

(3.4)表达式类型的自动提升
表达式:用各种运算符±*/,把变量啊,常量啊放在一起做运算; 3+5

当一个算术表达式包含多个基本类型值时(哪怕相同的类型),整个算术表达式的数据类型将发生自动提升:【Java自动提升规则】
a)所有byte,short,char类型自动提升到int类型;
b)整个算术表达式的数据类型自动提升到与表达式中最高等级操作数同样的类型;【图中:左边最低,右边最高】

(四)算术运算符、运算符优先级

一:Java的运算符
一些特殊的符号; 加减乘除 , 大于号,小于号
a)算术运算符 +,-,*,/ ,%
b)关系/比较运算符
c)赋值运算符
d)逻辑运算符
e)位运算符
f)其他运算符

二:算术运算符
+:加
-:减
:乘 System.out.println(5.14.6); //23.46,实际=23.459999999999997
结论:浮点数做乘除法运算的时候他可能不是那么精确;
/:除 30/3
两个整数相除,结果会舍弃小数部分;
其实,因为精确度问题,所以乘除这种运算符,很少 用于浮点数之间,一般都是用于整数之间的;
%:取余 20 %3 : 第一个除数除以第二个除数,得到一个结果,没有整除开的会得到一个剩余的值,整除开的就得到0这个余数;
因为内部也涉及到除法操作,所以,如果两个操作数都为整数,那么第二个操作数也不能为0;
对0.0取余会得到一个非数NaN,浮点数对0取余也会得到一个非数NaN
如果第一个操作数为0,则整个取余结果为0或者0.0【看第二个操作数是否为浮点数/整数】
如果第一个操作数为0.0,则整个取余结果为0.0

三:运算符优先级
先乘除,后加减 ,如果优先级相同,那么就先算左边,后算右边【从左到右结合】;
2+3*5 = 17
2+8-5 = 5
2+(8-6) = 4
特别说明:当记不住优先级的时候,那么就用()来指定优先级,因为()的优先级是最高的,所以()可以改变优先级;

四:自增自减运算符
自增: ++ ,给自己+1【自己的值改变了】 ,针对于一个变量;
自减: – ,给自己-1【自己的值改变了】
范例观察:a++和++a,效果 一样 ; a–和--a效果一样

如果你单独一行代码里只有 a++或者++a,那么这两行代码没区别;
如果你单独一行代码里只有 a–或者–a,那么这两行代码没区别;

如果我们把自增自减运算符放到其他的语句【表达式】中,就不一样了;
a)a++属于:先用后加 ,也就是说先用a的值跟周边的表达式/语句做运算,然后再把自己的值+1;
b)++a属于:先加后用,也就是说,先把自己的值+1【自己的值发生了改变】,然后再用发生改变后的这个值;

–是完全一个道理;
a)a–属于:先用后减
b)–a属于:先减后用

最终,只要执行++或者–,那么变量a的值肯定 会发生变化;

自增和自减运算符只能用于变量,不能用于常量也不能用于表达式;
自增和自减常用于整型变量中,用于给变量自身+1,-1,日后学习循环语句时我们再进一步研究;

(五)赋值运算符、复合赋值运算符

一:赋值运算符
= :将等号右边的值赋给等号左边的变量,这里注意,等号左边,是个变量【不可能是个常数】;
所谓赋值,理解成 给变量一个值,或者 改变变量的值
赋值运算符优先级非常低
x = 3+5;

二:复合赋值运算符
在赋值运算符 = 之前加上其他运算符,构成复合赋值运算符
a += 3; ============> a = a + 3;
x *= y+8; ============> x = x * (y+8); *=优先级低于 +
x /= y+8; ============> x = x / (y+8); /=优先级低于 +
x %= 3; ============> x = x % 3;
复合赋值运算符计算的路子:
1)把最左边的变量单独写下来 a
2)把整个式子中的等号去掉 a + 3
3)把赋值运算符涌上来,用来连接1)2),最终得到的写法是a = a + 3; 得到一个赋值语句;
所以复合赋值的效果最终也是赋值的效果;

三:赋值表达式的值
x = 3
赋值表达式本身也是有值的,值就是 = 右侧的值
传统运算符结合顺序一般是从左到右, 3+4 + 6,但是赋值运算符结合顺序确是从右到左
a = b = c = 5;
强烈不建议把 a += ( a-= ( a*a ) ); 这种如此复杂的表达式写成一行,老师建议拆解成多行写,就非常清晰。

三、程序执行流程控制

(一)程序的三种基本结构

一行Java程序末尾加一个;就构成 一个语句;
整个程序的执行是以main函数为入口点,顺序的从上往下执行各条语句;

一:顺序结构
从上到下,顺序逐行的执行各条语句的执行步骤,就是顺序结构;写在前面的代码先执行,写在后边的代码后执行;
看图:先执行A操作,再执行B操作;

二:选择结构【也叫:分支结构】
(2.1)二选一
看图:条件为真的时候执行A操作,否则执行B操作;只能执行A和B两种操作之一,不能同时执行A操作和B操作;

(2.2)多选一【也叫多选择分支结构】
看图:有多个 程序执行的 分支【A1…An】,但执行的时候只会执行某一个分支;

三:循环结构:【循环多次的去执行一条/一堆 语句】
(3.1)当型循环结构
看图:先判断条件P,才决定是否执行A操作;当条件P成立时,反复的执行A操作,一直到P为假的时候才停止循环。程序从下边的出口出去了;
如果条件P第一次判断就不成立,那么A操作就一次也不执行;

(3.2)直到型循环结构
先执行A操作,然后再判断P条件是否为真【是否成立】,若为真则继续执行A操作,一直到P条件为假;
我们看得到,A操作至少会被执行一次【这一条是和当型循环结构最本质的区别】;

日后建议大家在程序设计中,始终应该采用这些结构化的程序设计方法,养成良好的程序开发习惯;

(二)关系运算符、逻辑运算符

一:总述与回顾
已经讲过的:算术运算符,赋值【复合赋值】运算符

二:关系运算符
关于关系运算符优先级,我们强调一下;
a)如图,前四个关系运算符的优先级相同,后两个关系运算符的优先级相同;前四个运算符的优先级要高于后两个运算符的优先级;
b)关系运算符的优先级低于 算术运算符;
c)关系运算符的优先级高于 赋值【复合赋值】运算符的;
d) == 千万不要写成 = ,否则导致语法错误
e)关系运算返回的结果是一个boolean类型【布尔类型】,其值只有两种,true【真】 false【假】
f)后两种关系运算符 == 和 != 适合所有的基本数据类型之间使用;而前四种不能用于boolean类型之间的运算,因为boolean类型只有false,true两种值,没有比较大小的意义;

(2.1)关系表达式
用关系运算符把两个表达式连接起来的式子就叫关系表达式;
a>b
a<=c
关系表达式的结果/值 是一个逻辑值,也就是上边说的布尔类型值【true/false】
5 == 3 =========>false
5 >= 0 =========>true

三:逻辑运算符
逻辑表达式:用逻辑运算符 将 关系表达式 连接起来 构成了逻辑表达式;
如果我希望 a>3 并且 b>4,那么这里这个 “并且”,就是个逻辑运算符;
同时 “a > 3 并且 b>4” 就是逻辑表达式;
逻辑表达式的值也是 : 真true,假false;

(3.1)三种常用的逻辑运算符&&、||、!
&& 和 || 是 双目运算符,也就是需要有两个运算量【左边一个,右边一个】;
a>3 && b>4
a>3 || b>4
!true =====>false
!false ======>true

总结: a代表一堆表达式,b代表一堆表达式
a && b :若a和b都为true,那么a && b 才为true,否则false; ----全true出true,有false一定出false;
a || b :若a或b有一个为true,那么a || b就为true,否则false;-----有true就出true,全false才出false;
!a :若a为true那么 !a得到的就是false,若a为false,那么!a就是true

特别注意:
a)千万不要把 && 写成 &,否则语法上不报错,但是结果不一样,后续我们会讲解 &
千万不要把 || 写成 |,否则语法上不报错,但是结果不一样,后续我们会讲解 |

逻辑运算中的&&和||优先级低于关系运算符,但是逻辑运算中的!高于关系运算符;

(3.2)短路求值以及&和|逻辑运算符
在逻辑表达式求解中,其实并不是所有逻辑运算符都会被执行,只有在必须执行下一个逻辑运算符才能求出表达式的结果时,才执行该运算符;
a && b && c //只有a为true才需要判断b,只有a,b都为true才需要判断c
//只要a为false,那么就不必判断b,如果a为true,b为false则就不需要判断c

所谓短路求值,就是后边这个逻辑表达式不需要执行的情况下,整个表达式的结果就已经能够求得了,节省了不必要的计算开支,从而提升性能;
所以Java语言中,采用了短路求值;
严肃面对短路求值,以免写出错误的代码;

引入两个额外的运算符;
a)& :与。不短路 与 ,作用和 &&完全相同,但是不进行短路求值;
b)| :或。不短路 或, 作用和 ||完全相同,但是不进行短路求值;

(3.3)异或逻辑运算符^
^:异或【数字6上边的符号】,两个操作数不同的时候 返回真,两个操作数 相同的时候返回假;

(三) 选择结构之if语句详解

一:回顾
顺序结构,选择结构【二选一,多选一】,循环结构

选择结构【二选一】:if语句

二:if语句的三种形式
if语句是用来判断给定的条件是否满足,根据判断结果【true,false】决定执行给出的两种操作之一;

(2.1)单独一个if语句
格式: if(表达式) 语句
含义:如果表达式中的条件满足(结果true)则执行该语句;
如果想在条件成立的时候 执行多条语句,则必须用{}构成复合语句,{}括起来以后,就是一个整体了,条件成立,则这个{}里边的语句就会全部被执行,
如果条件不成立,那么这个{}里边的语句就全部不执行;
如果不加{} ,则if语句条件成立时所能够执行的语句知识第一个;之前的语句;
而第一个;之后的语句跟if条件没有任何关系了【不管if条件是否成立,都会执行第一个;之后的这些语句】;
建议:就算if条件成立只执行一条语句,也建议把该语句放入到一个{}中括起来,这样非常清晰;

(2.2)if,else配对使用
格式: if(表达式) 语句1 else 语句2
含义:如果表达式中的条件满足,则执行语句1,否则执行语句2,语句1和语句2中有且只有一个能执行;
else必须要有配对的if

结论:if语句可以单独使用,但是else语句不能单独使用,必须与if语句配对使用【必须是if结束后紧跟的就得是else】,也就是说,如果出现了else,则必然会对应一个if语句;

(2.3)if,else if,else配合使用
格式:if(表达式1) 语句1
else if(表达式2) 语句2
else if(表达式3) 语句3

else if(表达式m) 语句m
else 语句n

这里else if可以有0个或者多个,当然如果else if有0个的话,那么这种格式就相当于(2.2)
含义:如果表达式1成立,则执行语句1,如果表达式2成立,则执行语句2.。。。。。。。如果表达式m成立,则执行语句m,如果表达式1…m都不成立,则执行语句n;

三:if语句的嵌套
在if语句中又包含一个或者多个if语句称为if语句的嵌套;
if(…)
if(…) 语句1
else 语句2
else
if(…) 语句3
else 语句4

if与else的配对关系,else总是和它上面最近的尚未配对的if语句进行配对

if()
if()语句1
else
if() 语句2
else 语句3 -------------------------X

if()
if()语句1
else
if() 语句2
else 语句3

如果if和else数目不一致的话,为了防止出错,建议增加花括号{}来配对关系;
if()
{
if()
{
if()
{
语句;
}
}
else
{
语句

}

}
else
{
}

四:空语句,讲解的目的就是大家碰到这种代码得认识;
空语句:就是一个分号

(四)条件运算符和switch语句

一:条件运算符 ?:
if,else配对使用;

条件运算符 也叫 三目运算符【三元操作符】,它需要三个操作数,一般来讲,几目运算符就需要几个操作数;
条件运算符干的事就是if,else语句的精简写法;

(a > b)?a:b如何执行?
如果 (a > b)条件为真,则条件表达式取a的值作为整个值,否则取b的值作为整个表达式的值
一般形式: 表达式1?表达式2:表达式3

条件运算符是从右到左的结合顺序,所以:
a > b?a: c>d?c:d ====> a > b?a: (c>d?c:d)

条件运算符的优缺点:
a)条件运算符比较简单,写起代码来比较简洁,这是他的优点,但是虽然代码简洁,但可读性比较差,这是他的缺点;
b)虽然条件运算符和if,else类似,但是if和else语句当条件成立或者不成立的时候,是可以执行一堆语句的,只需要把这一堆语句用{}括起来即可;
但条件运算符无法支持多语句,它只能简单的支持一个结果值,这也是它使用中的一个很大的局限性;
c)其他的内容,大家在使用中自己感受和总结一下;

二:switch语句
格式:
switch(表达式)
{
case 常量表达式1:
1行或者多行语句;
break;

case 常量表达式2:
1行或者多行语句;
break;

case 常量表达式n:
1行或者多行语句;
break;
default:
1行或者多行语句;
break;
}
解释:switch后面表达式的值若满足任何某个case后面的常量表达式的值,则执行该case后面的 1行或者多行语句,一直遇到break停止,跳出整个switch语句;
如果所有case条件都不满足,则会执行default中包含的 1行或者多行语句,然后跳出整个switch语句;
上述格式中,常量表达式的值一般来讲是byte,short,char,int这几种整数类型;char也看成是整数【对应着字符的ascii码】

几点说明:
a)switch后面的表达式的类型,除了这几种基本的整数类型【byte,short,char,int】,还可以是字符串或者枚举类型【枚举类型以后再讲】,
字符串 类型后续会给出一个范例;
b)每个case后边的常量表达式的值必须互不相同,不然会出现编译错;
c)各个case语句之间,case和default语句之间,顺序并没有影响,谁在上面,谁在下面无所谓;
d)绝不要忘记break,否则就会出问题;
结论:绝对不要忘记,每一个switch语句中case条件之后都应该跟一个break语句,除非你有特殊需求;【break语句的能力是使程序执行流程跳转到switch语句后边去】
e)case后边如果有多行语句,则不用给这多行语句加{} ,程序会自动顺序执行本case后面的所有语句。当然你人为的加{}也可以;
f)default可以没有,那么当所有case条件都不满足,则整个switch就啥也没执行【直接跳过switch语句】;
g)多个case 可以共用一组执行语句,只需要这几个case语句连在一起写;

switch中使用字符串的简单范例

(五)循环控制概述,while、do while精解

一:循环控制语句概述
顺序结构,选择结构【if,switch】,循环结构【当型循环结构while,直到型循环结构 do while】…for

二:while语句:循环结构【当型循环结构】
一般形式:
while(表达式) 要执行的语句
特点:先判断表达式的值,如果为true则执行语句;补充:如果条件第一次就不满足,则循环内的语句一次也不执行;

范例说明:
a)如果while条件成立的时候要执行的语句不止一条,则用{}把要执行的所以后语句包起来;
b)在while语句中,一定要有促使整个while条件 不成立 的因素【语句:i++; 】存在;
c)如果第一次while条件不成立的话,那么while中的语句就一次也不会执行,直接跳出while循环【直接跳到while语句后边去执行】
d)千万不要再while( i <= 100)后边直接加;,这是错误的,新手尤其容易犯这个错误;

三:do while语句:循环结构【直到型循环结构】
一般形式:
do 要执行的语句 while(表达式);
特点: 无条件的 先执行 一次循环体内的语句,然后判断表达式的值,如果表达式的值为真时,继续执行循环体内的语句,然后继续判断表达式的值…,如此反复
一直到表达式的值为假,跳出循环体;

当型 与 直到型循环结构 的最大不同在哪里?
当型:条件若不满足,则循环体内的语句一次也不执行【说白了,循环体内的语句存在一次也不执行的可能性】
直到型:至少会执行循环体内的语句一次,然后才判断条件是否满足,如果条件满足,则继续执行循环体内的语句;

结论:当while后面的表达式第一次值为真的时候,两种循环得到的结果相同,否则,两种循环得到的结果不相同;

(六) for语句精解,变量的作用域

一:for【循环语句】语句的一般形式
能够取代while,do while语句,但是否取代,取决于大家的使用习惯

一般形式:
for(表达1;表达式2;表达式3) 内嵌的语句【循环体】;
执行步骤:
a)求解表达式1【执行表达式1】
b)求解表达式2【执行表达式2】,结果是个boolean:真,假
b.1)若值为真:则执行for语句中指定的内嵌语句【多条可以用{}括起来构成复合语句】,同时求解表达式3,反复循环步骤2,一直到表达式2的值为假
b.2)若值为假:则循环结束,跳到for语句后边去执行;

注意:表达式1只会被执行一次;表达式2、3,内嵌语句可能被执行多次

for语句最简单最常用的应用形式如下:
for(循环变量赋初值;循环变量结束条件;循环变量增加值) 内嵌的语句;
内嵌的语句:如果是多条,可以用{}构成复合语句; 如果只有一条,就可以不用{}括起来,那么此时for循环成立时所执行的语句就只到第一个分号那里为止;

二:for语句的实际操练
演示:1+2++。。。100

三:for语句主要说明
(1)表达式1可以省略,但后边的 ;不能省略,当表达式1省略时,应该在for语句之前给循环变量赋初值;这种写法少见,但大家要认识;
(2)表达式2可以省略,但后边的 ;不能省略,也就是不判断循环条件,那么循环就会无终止的进行下去。此时,我们必须用break【直接跳到for之后】终止循环;
break后续详细讲;
(3)表达式3可以省略,我们要想办法保证循环能够正常结束,否则循环会无终止的进行下去;
(4)表达式1、表达式3能同时省略,只给表达式2;注意前后分号都不能省略
(5)三个表达式全部都省略;但分号不能省略:相当于不设置初值,不判断条件(认为条件为真),循环变量也不增加。会导致整个循环无终止的执行。
for( ; ; )
{
}
类似于
while(true)
{
}
(6)表达式1可以是设置循环变量(i)初值,也可以是与循环变量无关的其他表达式【表达式1只会被执行1次】
(7)表达式1和表达式3都可以是简单的表达式或者是带逗号的表达式。【表达式3可能一次也不会被执行,也可能会被循环多次执行】
逗号表达式无非就是用都逗号把一些表达式分开而已,这些表达式都会独立的执行;
用逗号表达式的场合不多:a)用来定义多个变量 b)for语句中的表达式1,表达式3;

总之:建议for语句用最规矩的方式使用;
int i;
for(i = 0; i <= 100;i++)
{
//循环体…
//i ++; 既然表达式3存在,那么咱们一般无需在循环体内再修改循环变量的值;

}

四:变量的作用域【变量能够使用的范围】简单说
二章一节讲解过,变量要先定义,后使用;
在for循环表达式中【for循环体内】定义的变量,作用域仍旧限制在for循环内,不可以跑出for循环;

如果一个变量定义在{}内,那么这个变量的作用域就从定义的行开始,一直到}结束

(七)循环的嵌套、控制,return语句

一:循环的嵌套
while,do while,for
循环的嵌套:一个循环体内又包含另外一个完整的循环结构,称为循环的嵌套【循环套循环】;
套的层数多了就叫多层循环;

举例:9,9乘法表,输出
11 = 1
2
1 = 2 22 = 4
3
1 = 3 32 = 6 33 = 9

91 = 9 92 = 18…9*9=81
外循环每执行一次,内循环就要执行多次;

二:几种循环语句的比较
while,do while,for
a)多数情况下,这些循环之间可以互相替代;
b)while,for是先判断表达式的值,然后执行语句。do while先执行语句,再判断表达式的值,所以循环体内的一系列语句至少被执行一次;

while语句的一般形式:
while(表达式) 要执行的语句

do while语句的一般形式:
do 要执行的语句
while(表达式);

for语句的一般形式:
for(表达式1;表达式2;表达式3) 要执行的语句

c)对while,do while for这三种循环语句,可以用break语句跳出循环,用continue语句结束本次循环;

三:循环结构控制之break、continue语句
(3.1)break语句
回顾:switch语句中,用到了break,break的作用是跳出switch语句,执行switch后边的语句;
同时:break还可以用在三种循环while,do while,for语句体中,用于跳出循环体;也就是提前结束循环【不执行循环中剩余的语句】,接着执行循环后边的语句;
演示:计算1+2+3+…100的和值,和值其实是5050,当和值达到4000的时候,我需要退出整个循环;

break语句不能 用于循环语句和 switch语句之外的任何其他语句中;
break只能跳出break语句所在的这层循环【这层循环是break的父亲】;

break 标签;
总结:通常紧跟在break之后的标签,都必须在break所在的循环的外层循环之前定义才 有意义;

(3.2)continue语句
continue只能用在三种循环while,do while,for语句体中;
continue语句作用:结束本次循环,continue之后的语句在本次循环中不会被执行到【忽略本次循环中剩下的语句】,接着进行下一次是否执行循环的判断;

continue和break的区别:continue只能结束本次循环,而不是终止整个循环的执行。而break语句是结束整个循环的执行,跳转到整个循环的后边的语句去执行了;

演示continue;
把1到100之间不能被3整除的数输出出来
continue 标签;

goto 标签;
慎用【continue 标签;】以及【break 标签;】

四:return语句
return:返回,
作用:当执行return时,从方法【函数】中返回,也就是退出这个方法【函数】了;

一旦遇到return,整个当前这个方法都执行结束了;return很霸道,比break霸道多了;
不管嵌套多少层,不管return语句在哪个位置,只要被执行,那么整个程序的执行流程,都会从当前的方法中退出【返回】,
如果当前是从main方法中return,会导致整个程序执行结束;
以后讲解 类,对象,方法 ;

四、数组

(一) 数组的定义、初始化、使用

一:定义数组
byte,short,int,long,char,float,double;

int a;
int a,b,c,d,e,f,g…,aa,bb,cc,
引入数组,解决一次定义若干个变量的问题;
我们定义一个数组,就等价于 定义了一堆变量,或者大家也可以把数组理解为一堆变量的集合;

针对一次定义出100个整型变量这个问题,用数组定义;
变量和数组一样:先定义,后使用;
定义数组的格式:
a)类型名[] 数组名;
b)类型 数组名[];
数组名:正常的标识符;谈到数组,总是离不开中括号[];

总结:一个数组中存储的数据只有一种数据类型,不能是多种数据类型;

二:初始化数组
数组要想使用,必须是a)先定义,b)再初始化
初始化数组实际上是干了两个事:
a)指定这个数组中元素的多少;刚才说,把数组理解成一堆变量的集合,那这一堆是多少个【数组中有多少个元素】,必须通过初始化数组来制定出来;
a.1)计算机内部会根据你指定的这个长度来分配存储这些数据的内存空间;
a.2)因为数组的存储空间在 a.1)中已经分配出来了,因此也就意味着该数组的长度不可以再改变了;
b)给每个数组元素赋初值;

初始化数组的两种方式:
(2.1)静态初始化:适合于初始化数组元素数量比较少的情况
静态初始化:初始化时由程序员指定每个数组元素的初始值,系统自动根据这些初始值来计算和决定数组的长度;
格式:
数组名 = new 类型[] {元素值1,元素值2,…};

实际上,数组的定义和初始化是可以写在一起的,一行就可以搞定,老师强烈建议用一行搞定的写法;

(2.2)动态初始化
动态初始化:初始化时只指定数组长度,由系统为数组元素分配初始化值[系统会给数组元素分配缺省值];
格式:
数组名 = new 类型[元素数量];
null:“空”,没有指向任何一个对象【东西】的时候,就可以说成是它指向一个null;

数组给初值:
float缺省给0.0
boolean缺省给false
char缺省给\u0000 ==> 0
int缺省给0
String缺省给null

总结静态初始化和动态初始化:
a)静态初始化是不需要指定数组大小但需要指定数组初值的;
int Aarr[] = {10,20,30,40,50};
b)动态初始化是需要指定数组大小但不需要指定数组初值的;
float Barr[] = new float[100];
c)不要再进行数组初始化的时候,又指定数组长度,又为每个数组元素分配初值,否则会导致语法错误;

三:使用数组
(3.1)总述
int a[] = new int[10];
a)定义一个数组,名字叫a
b)我们这个数组中,有10个元素,每个元素都是int类型
c)若要引用数组中的任何 一个元素,
格式:数组名[下标]
下标:从0开始,这10个元素就是a[0],a[1],a[2]…a[9],千万千万要注意,不包括a[10],否则执行的时候会报程序异常[编译时不报异常];
如果是: int a[] = new int[100]; 那么这100个元素 a[0]…a[99]

(3.2)访问数组元素
给数组元素赋值,打印数组元素啊,进行各种运算,都属于访问数组元素;

(3.3)获取数组长度
数组有一个属性叫length,通过这个属性,我们就可以访问数组的长度;

(3.4)foreach循环
有一种更简洁的for语句的写法,foreach循环;一般来讲用于打印数组元素;
在使用foreach写法的时候,我们不需要指定数组长度,foreach本身就能够自动的遍历数组里的每个元素;
格式:
for(类型 变量名:数组名)
{
}

a)大家不要误解,foreach这是个叫法,实际写代码的时候还是个for语句,只是叫法特殊了一点;
foreach写法特殊,与传统for语句不一样,大家需要特殊去记;
foreach一般用于输出数组,或者容易内容的时候使用,其他场合很少见到;容器以后讲解

(二) 数组的存储,栈、堆

一:数组赋值
一个数组的存储空间分配出来了,那么就意味着该数组的长度不可以改变了;

二:数组的存储结构
数组名【箭头/指针】 实际上 是指向 一个位置【数组名代表内存中的一个地址】
是吧arrB赋值给了arrA,从而导致arrA指向了arrB所指向的的位置;
黄色区域有两个箭头指向它【两个引用】;
蓝色区域内存变成了垃圾内存,后续由Java垃圾回收器回收以再利用;

总结arrA = new int[]{10,20,30,40}; :
a)给数组分配空间,因为里边有四个数,所以分配的空间【紧挨着】正好是能够保存下这四个数字的;系统分配了蓝色这么大的空间,用来保存这四个数字;
b)把这四个数字10,20,30,40分别放到了对应的内存空间中去;
c)让红色块指向蓝色区域的首地址,为了让红色块能够指向蓝色区域的首地址,实际上只要把蓝色区域的首地址保存在红色块中,自然就等于红色块指向了蓝色块的首地址。
所以为啥多出来 一个红色块,就是为了保存蓝色块的首地址,从而达到能够指向蓝色块首地址这个目的的;
综合来看:红色块,蓝色块都需要占用内存;

三:栈和堆:重要,要求大家必须理解
老师讲啥你会啥;
(3. 1)栈和堆的基本概念
Java中,大概有5个地方都能够存储数据,其中我们目前只需要关心两个地方,一个栈/堆栈【栈内存】,一个叫堆【堆内存】
栈/堆栈:stack
堆:heap

(3.2)栈和堆的使用时机
栈内存:在方法中定义了一些基本类型的变量和对象的引用变量【比如数组名】,这些变量都是在栈内存中分配空间;
当超过这些变量的作用域【三章六节】后,Java会自动释放掉为该变量分配的内存空间,该内存可以立即被另外他用;
总结:跳出方法【方法执行完毕】后,在这个方法中分配的所有栈内存会被回收以备将来挪作他用;
所讲解的红色的数组A这个内存,也是分配在栈上的;

堆内存:堆内存用来存放由new创建的数组和对象,在队中分配的内存,有Java虚拟机JVM的垃圾回收器不定期的回收;

数组名 就成为了 引用变量;
引用变量 就是 一个普通变量

arrA = null; 会导致arrA所指向的蓝色内存被打断了,这样就导致蓝色内存没有 引用变量 指向他了;就会导致蓝色内存变成垃圾了,由垃圾回收器在后续不定期的回收;
这种语句的效果往往就是提醒垃圾回收器尽快回收 该引用变量所指向的内存;

(三) 数组维数、二维、多维数组

五、类和对象

(一) 语言特性、Java程序结构回顾

一:语言特性:过程式、对象式程序设计
Java是一种面向对象的程序设计语言;

(1.1)面向过程式的程序设计
常规写代码的方法是:从上往下,逐步求精,按顺序一步一步的把问题解决;
代码一行一行写,事情一件一件办;

(1.2)基于对象的程序设计 和 面向对象的程序设计
没有离开 “对象” 这个概念,解释 “对象”;

“类”(比如一个学生:学号,名字,性别,成绩。。。。。。,这些属性我们把他搞到一块弄成一堆数据),“学生类”【也看做一个类型】;
在类中,不仅可以定义的类属性【类的成员变量】,我们还可以定义属于这个类的方法【函数】,来实现一些功能;
“对象”:在Java中,如果我们要使用 学生类 的话,我们就要定义 一个属于该类的变量 ,这个变量我们叫 对象;

class Dagongzi //定义一个打工仔类
{
//里边有一些方法【函数】
void qichuang();
void chuanyifu();
void shuaya();
void chizaofan();
void shangban();

  //提供对外的一些供调用的调用方法:
  void tufashijian(int eventtype); 

}

Dagongzi zhangsan; //定义一个属于该类的变量【对象】

zhangsan.qichuang(); //通过Dagongzi类的对象来调用该类的方法;
zhangsan.chuanyifu();

int myi;
myi.qichuang(); //错误的;

基于对象的程序设计:我们把这种——把功能包在类中,需要的时候通过定义一个 类变量【对象】的方式来调用类中的方法,
这种程序的书写方式,我们就叫做基于对象的程序设计。

a)继承性:
class Tuixiaoyuan【子类】 ,继承自Dagongzi【父类】
{
void changguoge();

void shangban();

}
这种类之间的继承关系:继承性

b)多态性:父类和子类都 有一个同名函数,到底调用的是父类的同名函数还是子类的同名函数,是有一些说法的,这个说法就是所谓的多态性;

面向对象的程序设计:当我们把继承性和多态性技术融入到我们程序设计中去的时候,那么我们 基于对象的程序设计就升华了,也就是更牛了,逼格更高了,这个时候就叫做:
面向对象的程序设计

Java支持面向对象的三大特性:封装性【权限控制:哪些方法能够被外界调用,哪些方法只能够在本类的方法中被调用】,继承性,多态性;

基于对象的程序设计 和 面向对象的程序设计 主要区别:在基于对象的程序设计中额外运用了 封装性、继承性、多态性技术,从而变成了面向对象程序设计;

面向对象程序设计的优点:
a)易维护:
b)易扩展:继承性、多态性,可以达到少写很多代码,实现很多变化;
c)模块化【封装性】,保证了数据的安全;

面向对象程序设计其实就是一种程序设计理念,给我们程序设计带来便利,大大的提高工作效率;

二:Java的程序结构
一章二节
(1)java源码文件的扩展名必须是.java
(2)强烈建议一个.java源代码文件中只定义一个类,这个不是必须的,但希望大家遵守;
(3)java源文件可以有多个,a.java ,b.java;

总结:
(1)Java 是一种面向对象的程序设计语言
(2)Java支持面向对象的三大特性:封装、继承、多态;
(3)Java程序的基本结构:文件包着类,类包着方法,方法包着语句;

(二)类定义,成员变量、方法

一:类综述
类:是一种我们自己定义的 数据类型;

设计一个类的思考角度:
a)站在设计和实现者的角度来考虑;
b)站在使用者角度来考虑:
c)父类;

二:类基础
(2.1)定义一个类【创建一个类】
说明:
a)一个类就是一个用户自己定义的数据类型,我们可以把类想象成一个名字空间,包着一堆东西,比如人类,就是一个类;
b)一个类的构成: 成员变量、成员方法【成员函数】;
创建一个类的格式:
[修饰符] class 类名
{
//成员变量…若干个【0…N】
//方法…若干个【0…N】
}

强烈建议:一个java源文件中只定义一个类;
我们即将创建一个Time类;
格式中带[]的部分可以去掉【去掉也符合语法规则】;

格式说明:
a)修饰符可以干掉
b)类名:只要是个合法的标识符就可以

(2.2)成员变量和方法的书写
成员变量/属性/字段
格式:
[修饰符] 类型名 成员变量名 [=默认值];
说明:
a)[修饰符]中的内容可选,也就是说可以去掉,或者说可以省略
b)成员变量名:只要是个合法的标识符就可以
c)[=默认值] 也是可选的

方法【成员函数】的概念:在程序设计中,我们经常将一些常用的功能模块编写成方法(一段一段包装起来的代码),目的就是减少重复编写程序的工作量;
成员变量,方法【成员函数】在类中的定义顺序无所谓,谁在上谁在下都行;

定义方法的格式:
[修饰符] 方法的返回值类型 方法名(形式参数列表)
{
//零条到多条语句构成的方法体
}
形式参数列表:零到多个 形式 参数,如果是多个,则彼此之间用逗号分隔
参数的概念:所谓参数就是,我们调用方法的时候,希望把一些数据传递给该方法,这个时候,该方法就需要用一些变量来接收这些数据;
这些接收数据的变量,就叫参数;参数一般分为两种:形式参数【形参】和实际参数【实参】;
格式说明:
a)方法名:只要是个合法的标识符就可以
b)形式参数列表:可以是0个,也可以是多个,如果是多个,彼此之间用逗号分隔;
c)方法的返回值类型:如果方法没有返回值,则方法中不需要包含return语句;而且在返回值类型这里,我们要写上void【代表这个方法没有返回值】

(三)对象的创建、对象的存储结构

一:创建一个类对象
Dagongzi ,zhangsan
Dagongzi zhangsa;

类:a)想象成一个名字空间,包着一堆东西;
b)一个集合,一个概念性的东西【Dagongzi】,对象 理解成一个具体存在的实体【实例】[zhangsa];
要调用类中的各种方法,必须通过类的对象来调用;

创建类对象分三步走:
a)定义类对象;
格式:类名 对象名;
对象名:主要是个合法的标识符即可
b)初始化类对象:光定义类对象还不行,必须要初始化这个类对象,然后这个类对象才能够被使用
格式: 对象名 = new 类名();
c)使用类对象
类对象一旦定义好之后,我们就可以通过这个类对象来:
c.1)访问这个类中的成员变量;
访问方法: 对象名.成员变量名
c.2)调用这个类中的方法;
访问方法: 对象名.方法名(实际参数列表); 实际参数列表中参数数量可以为0个或者多个,取决于该方法定义时具体的形参数量;

在实际的应用中,成员变量是跟着对象走的,隶属于某个类的对象;
调用方法的参数说明:
(1)实参数量和类型要与形参数量和类型保持一致;
(2)实际变量对形参变量的数据传递是“值传递”,也就是把实参的值传递给了形参;

总结:类中的成员变量和方法【成员函数】的调用都非常简单:定义并初始化一个类的对象,然后,用这个类对象就可以直接操纵成员变量和调用类中的方法;

二:对象的存储结构
数组名:四章二节,把数组名 称为“引用变量”【感觉象一个箭头一个指针】
这里,对象名【对象】,其实也是引用变量;

Time mytime = new Time(); //定义并初始化类对象mytime
上边代码产生了两个东西:
a)产生了一个mytime变量【引用变量】,在栈里;
b)一个Time对象,在堆里;
c)让mytime这个引用变量指向了Time对象;

Time mytime = new Time(); //定义并初始化类对象mytime
Time mytime2 = new Time();
mytime和mytime2这两个对象,每一个都有自己的在栈中的内存和在堆中的内存;而且这些成员变量的值【内容】都是保存在堆内存里的;
同时,栈中保存的是所指向的堆中对象内容的首地址;

C语言中:指针,实际上Java中这个引用变量其实就是C语言中的指针;但是因为指针概念比较繁琐,所以java中不提指针这个概念;
如果希望把980这块内存当成垃圾,那么我们就需要 切断 所有指向980这块对象内容内存的指针【箭头】

(四)构造器细说,this关键字

一:构造器细说
构造器 其实就是一个方法【成员函数】,构造器有一些特殊之处
(1.1)不带参数的构造器
构造器和常规的 方法 比有哪些特殊之处:
a)名字特殊:构造的方法名要求和类名相同
b)构造器不要写返回类型【不要加void】,也不需要返回值 ,如果你加了void作为返回类型,那么虽然语法上没问题,但是实际写出来的成员函数就不是构造器了;
c)构造器的能力就是:当你初始化一个对象的时候,构造器 会被自动调用,我们可以在构造器中增加一些诸如初始化成员变量等等的语句来实现成员变量的初始化;

不带参数的构造器有一个名字叫“默认构造器【无参构造器】”

(1.2)带参数的构造器
a)一个类中可以有多个构造器,只要各个构造器要么是参数数量不同,要么是参数的类型不同都可以;

(1.3)初始化对象报错分析
注意:
a)如果一个类中没有我们自己书写的构造器,那么系统会为该类提供一个默认的构造器【不带参数的】,这个默认的构造器就是不带参数的;
想象:Time(){},所以Time mytime = new Time();能编译和执行成功;
b)一旦程序员为一个类提供了构造器,那么 系统将不再为该类提供默认构造器;
c)不要手工调用构造函数,会报告语法错

二:this关键字
(2.1)this的概念
this:这个
this一般只出现在类的方法中;
this大家就理解成 指针/箭头/引用【引用变量】,this的真实身份,是一个隐藏起来的函数参数【形参】是属于系统偷偷加进来的参数,你看不到;
this含义:
a)this表示对“调用方法的那个对象”的引用
b)this指向调用该方法的对象
this其实就是指向这个 mytime 所指向的 对象【堆内存】的首地址;

(2.2)this的用途
a)一个方法调用另外一个方法;
int tmpvalue = this.GetHour();

成员变量跟着对象走的;
方法也跟着对象走;

b)方法的形参与成员变量名同名
this.Hour = Hour;

(2.3)this的强调
一般,我们不需要在代码中用到this,大家也许在日后的工作中能够看到包含this的这种代码;
但是一般来讲,我们很少直接用this;我们是隐式使用,多数情况下我们不需要摆到表面来使用this,这属于画蛇添足;

(五)static关键字详解

一:static关键字的基本概念
static关键字:“静态”;
a)用于修饰成员变量、方法等等;因此,一般会出现在定义成员变量或者方法的地方;
b)static修饰的成员变量 被叫做 静态成员变量 ,
属于这个类本身,而不属于该类的某个对象【某个实例】;
本章三节:普通成员变量是跟着对象走的,是隶属于某个类的对象【实例】的;
所有的静态成员变量,是共享同一块内存;
也可以用类名直接引用静态成员变量;

c)static修饰的方法叫静态方法【静态成员函数】。从此隶属于整个类,而不隶属于某个对象;
我们可以用任意一个属于该类的对象名或者直接用类名调用;
c.1)普通方法中是能够操作静态成员变量;
c.2)在静态方法中,你只能操作静态成员变量,不能操作 普通的成员变量;在静态方法中,没有this,不可以出现this
c.3)在静态方法中,你只能调用其他静态方法,而不能调用普通方法,因为普通方法是基于对象的,不是基于类的;
但是,在普通方法中,调用静态方法是没问题的;
一般建议用类名引用静态成员变量和调用静态方法,不赞同用对象名来操作;

总结:
a)有static修饰的,属于类本身,没有static修饰的,属于类对象【类实例】;
b)static修饰的成员变量和方法,即可以通过类调用,也可以通过 对象调用,没有static修饰的成员变量和方法,只能通过对象【实例】来调用
c)静态成员不能直接访问非静态成员,因为静态成员不支持this关键字,而非静态成员需要支持this关键字才能被访问;
d)尽量用类名而不是对象名调用静态成员变量和静态方法,以免造成混淆;

二:static内存分配
疑问:静态成员变量的内存什么时候分配,怎么分配的呢?
四章二节:Java,大概有5个不同的地方,都可以存储数据,我们介绍过 栈,堆;
这里介绍:共享数据区【方法区】,分为:
a)非静态区
b)静态区
在执行这个main的入口方法之前,KXClass这个类的各种数据/信息 就被载入到共享数据区中来了;

静态成员变量,只有一份,跟你new出来多少个对象没有关系,如果你是普通的成员变量,那么你new出来多少个对象,那么这个普通成员变量就有多少份;

总结:普通成员变量,属于对象,静态成员变量,属于整个类【非某个对象】

(六) 方法传参详解、方法重载

一:方法总述
方法:成员函数 ——把一些独立的功能写成一个一个的函数,方便被反复的调用;
c语言中函数:
void mfunc(){}
mfunc(); 调用
在Java中,所有方法【函数】必须写在一个类中 ,属于一个类的,没有办法独立存在;

二:方法的参数传递
(2.1)基本类型参数传递
方法中的参数叫:形参

结论:Java中参数传递的方式是“值传递”,也就说,把实参的值直接传递给形参,即便形参的值在方法中发生了变化,也不会影响到原来的实参的值;

(2.2)引用类型参数传递
数组,对象,提到过 “引用类型” ,这种类型的变量就好像一个箭头,一个指针一样,用来指向堆中某块内存的首地址;

引用类型参数传递能够改变实参中的内容,所以从这点来看,引用类型参数传递和基本类型参数传递是不一样的;

三:可变参数方法
调用该方法时,可以给0到任意多个实参,都可以正确调用该方法,这种方法就叫可变参数 方法
定义:在定义方法的时候,我们只需要在“最后一个形参”的类型名后边增加三个点… ,表明了该方法的形参可以接受多个参数值;
注意:一个方法中只能有一个可变形参,并且一定要处于形参列表的最后;
对于void VarArg(float fvar1,float fvar2,int… ivar3);前两个参数要给float类型,从第三个参数开始,我们可以给0到多个int型参数;

(3.1)数组名作为方法参数
(3.2)方法中读取可变形参
(3.3)数组名传递给可变形参
咱们可以把数组名当做可变参数传递给可变参的方法;

四:方法的重载
方法重载: Java允许在一个类中定义多个同名的方法,只要他们的参数列表不同【参数数量不同,参数类型不同,参数数量和类型都不同】,那么就称为方法重载;
方法重载和 方法返回值没有任何关系

(七) 方法的嵌套调用、递归调用

一:方法的嵌套调用
有些语言允许在方法内部再定义其他方法,这叫方法的嵌套定义【一个定义里边套着另外一个定义】;
Java语言是允许嵌套调用方法的;

二:方法的递归调用
(2.1)递归调用的定义
递归调用其实也是属于一种嵌套调用,只不过他是一种特殊【自己调用自己】的嵌套调用;

StackOverflowError:栈溢出【给你分配的内存资源用光了】,溢出就导致报错;

方法中的局部变量,方法的形参,方法的调用关系【现场】都需要保存在栈内存中;

递归调用有可能出现自己不断调用自己导致死循环;所以递归调用这种自己调用自己的方式必须要有一个出口,这个出口也叫做 递归结束条件;

总结递归:递归就是一个方法在它的方法体内调用它自身。执行递归方法将反复调用其自身,每调用一次就进入新的一层;
递归方法必须有结束条件[出口];

(2.2)递归调用的出口
范例:计算5的阶乘 :12345
a)我们不知道5的阶乘是多少,但是我们知道: 4的阶乘 * 5 就等于5的阶乘;
b)我们不知道4的阶乘是多少,但是我们知道: 3的阶乘 * 4 就等于4的阶乘;
c)我们不知道3的阶乘是多少,但是我们知道: 2的阶乘 * 3 就等于3的阶乘;
d)我们不知道2的阶乘是多少,但是我们知道: 1的阶乘 * 2 就等于2的阶乘;
e)根据数学知识,我们已经知道1的阶乘就是1;【递归调用的出口】

a)1的阶乘是1,可以作为出口,我们能够求出2的阶乘,也就是12
b)2的阶乘知道了,我们就能够求出3的阶乘,也就是2的阶乘
3
c)3的阶乘知道了,我们就能够求出4的阶乘,也就是3的阶乘4
d)4的阶乘知道了,我们就能够求出5的阶乘,也就是4的阶乘
5
最后,我们就得到了5的阶乘的结果

第一次是 result = dg_jiecheng(4) * 5; 然后进入到dg_jiecheng(4),这行被暂存;
第二次是 result = dg_jiecheng(3) * 4; 然后进入到dg_jiecheng(3),这行被暂存;
第三次是 result = dg_jiecheng(2) * 3; 然后进入到dg_jiecheng(2),这行被暂存;
第四次是 result = dg_jiecheng(1) * 2; 然后进入到dg_jiecheng(1),这行被暂存;

此时dg_jiecheng(1)的出口条件成立了,终于,能够执行return 1; 这可是return语句第一次捞着执行;
第一次retur 1,返回的是1,返回到哪里去了,返回到dg_jiecheng(2)这里;
result = 1 * 2; 并且也执行了return result,返回了12 = 2;
返回到哪里去了,返回到到dg_jiecheng(3)这里;
result = 2,并且也执行了 return result ,返回了 2
3
返回到哪里去了,返回到到dg_jiecheng(4)这里;
result = 6,并且也执行了 return result ,返回了 64
返回到哪里去了,返回到到dg_jiecheng(5)这里;
result = 24,并且也执行了 return result ,返回了 24
5
返回到哪里去了,返回到main()方法中;

(2.3)必须用递归吗,递归的优缺点
优点:
a)代码少,看起来简洁,也挺精妙

缺点:
a)代码虽然简洁,精妙,但是不好理解;
b)如果调用层次太深,调用栈可能溢出;如果真溢出,那么就不能用递归来解决该问题了;
c)效率和性能都不算高。这么深层次的调用,要保存的东西非常多;所以效率和性能高不起来;

是否必须使用递归:
a)有些问题,可以用可以不用;比如解决上边的5的阶乘问题;
b)有些问题,可能必须用递归解决【大家自己寻找答案】;汉诺塔
结论:具体情况具体分析,根据老师的经验,递归不常用,但是有些地方又不得不用,因为想不到更好的解决办法;

递归方法的直接和间接调用【作为了解】;
a)递归方法的直接调用:自己调用自己,这就是直接调用,上边讲过;
b)f1()方法调用了f2(),f2()方法中又调用了f1();

(2.4)实际运用简介
我方人员移动力是40,每移动一个格子,耗费的移动力是10,现在要求一下我方的人员能够移动到哪些位置;
int posx,int posy; //你当前的位置
int shengyuyidongli=40; //剩余的移动力

void pf_zhanqiFindPath(int posx,int posy,int shengyuyidongli) //递归调用
{
if(上边图块存在)
{
判断是否能走(比如是否有敌人,比如移动力是否够)
如果能走if
{
把能走的位置保存记录起来【要判断是否走到过该位置】,移动力进行适当的扣除
pf_zhanqiFindPath(…); //这就是递归调用
}
}
if(下边图块存在)
{
判断是否能走(比如是否有敌人,比如移动力是否够)
如果能走if
{
把能走的位置保存记录起来【要判断是否走到过该位置】,移动力进行适当的扣除
pf_zhanqiFindPath(…); //这就是递归调用
}
}
if(左边图块存在)
{
判断是否能走(比如是否有敌人,比如移动力是否够)
如果能走if
{
把能走的位置保存记录起来【要判断是否走到过该位置】,移动力进行适当的扣除
pf_zhanqiFindPath(…); //这就是递归调用
}
}
if(右边图块存在)
{
判断是否能走(比如是否有敌人,比如移动力是否够)
如果能走if
{
把能走的位置保存记录起来【要判断是否走到过该位置】,移动力进行适当的扣除
pf_zhanqiFindPath(…); //这就是递归调用
}
}
return;
}

这个递归调用的出口有两个,一个是 图块是否存在,不存在,就是一个出口;另外一个是否有剩余移动力也是出口;

(八)局部变量、成员变量总结

一:概念总述
(1.1)成员变量
a)普通成员变量,跟着类对象走,隶属于类的对象;也叫对象成员变量/实例成员变量; 访问方法:对象名.成员变量名;
b)静态成员变量【static】,跟着类走,隶属于类的,可以和叫静态成员变量/类成员变量;访问方法:类名.成员变量名【对象名.成员变量名】
构造并初始化一个类对象时,即便你不给该类对象成员初值,那么系统也会给该类对象的成员默认值;

(1.2)局部变量
a)形参:在方法中带的参数;
b)方法内定义的局部变量;以及代码块{}中定义的变量;

二:生存期
(2.1)成员变量
a)普通成员变量:对象被创建,对象成员变量就存在了,出了对象作用域,对象不存在了,那么该对象的成员变量也就不存在了【生存期跟着对象走的】
b)静态成员变量:当需要装载一个类的时候,这种成员变量就存在了;那什么时候消失呢?只有系统销毁这个类的时候,类的内存被回收了,静态成员变量才被销毁;
静态成员变量的生存期和类相同,都是非常非常长的生存期【生存期跟着类走】所以,一般来讲,静态成员变量的生存期,比普通成员变量变量生存期要长久得多;

(2.2)局部变量
a)形参:生存期贯穿整个方法;
b)方法内定义的局部变量:从定义开始,到整个方法结束
如果使用{}括起来的语句中定义的变量,那么从变量定义位置开始,到}结束;

注意:方法内定义的局部变量要给初值;
栈内存中变量所占的各种内存,是不需要垃圾回收系统去回收的,垃圾回收系统回收的内存其实是堆中的内存;
栈中的内存,当你的方法运行完成之后,自动被系统回收掉的;
java语言要求必须给局部变量一个初值;

对于写法:
int abc; //abc变量的分配时机
两种说法:
a)如果你纯定义一个局部变量【没给他初值】,那么系统不会为这个变量分配内存空间,当你后边给这个变量赋值的时候,系统才会给这个变量分配内存,并把赋的这个值放到这块内存中;
b)java 定义一个局部变量就会分配内存空间;【倾向于这种说法】

三:通过画图总结

(九)类的继承、super详解

面向对象程序设计的三大特性:封装性,继承性,多态性;Java语言支持这三大特性

一:如何进行类继承
父亲类,孩子类;
父类【基类/超类】;派生出孩子类【派生类/子类】
继承:既有父亲类,又有孩子类,这种层次关系我门就叫继承,孩子类能够从父亲类那里继承到很多东西;
我们可以把一些通用的东西放在父类里,然后每个孩子可以派生出自己独特的东西,还可以从父亲这里继承一些通用的东西,防止在子类中出现很多重复代码;

继承:先定义一个父类,父类中定义 一些公共的成员变量、方法,然后通过继承这个父类,来构建新的子类;
所以,通常来讲,子类会比它的父类有更多的成员变量以及方法,换句话说:子类一般都会比父类更庞大【占的内存更多,表达的东西更多】;
Human :父类 ,Men
子类继承父类的语法格式:
修饰符 class 子类名 extends 父类名
{
//成员变量若干
//方法若干
}

extends:扩展,把子类理解成对父类功能的扩展,也恰好符合咱们刚才说的:子类一般都会比父类更庞大

结论:执行 new Men()时,得到的结果是 —— 父类Human子对象部分被包含在了整个Men对象之中,其实Human子对象部分你完全可以理解成一个new Human()产生的结果;

二:覆盖父类的方法
在Java的类继承中,子类会覆盖父类中的同名方法;
子类会覆盖父类中的同名方法;
但是,如果子类中与父类同名的方法,但是参数不同【数量不同,类型不同】那么子类就不会覆盖父类中的同名方法

也有人把子类覆盖父类的同名方法也叫做 方法重载/方法覆盖【同名同参】;
方法覆盖适合于普通的方法,也适合于静态的方法;

三:调用父类的同名方法
格式:super.父类方法名(…);
super关键字:“超级”; 5章4节讲过 this关键字:this指向堆中的那个对象自己的;
大家可以把super理解成指向当前对象的父亲子对象的箭头或者指针,super一般也只是出现在类的方法中;
静态方法中,不可以出现this,同理也不可以出现super,有对象,才存在this,才存在super

四:覆盖父类的成员变量
在Java的类继承中,子类能够覆盖父类中的同名成员变量;

但是如果想访问父类中的同名成员变量呢?
a)可以用强制类型转换
通过这种把子类类型对象强制转成父类类型对象的方法,可以访问父类中的 成员变量/静态成员变量/被覆盖的静态方法;
但是无法通过此方法调用父类中的被 覆盖的普通方法;
不推荐使用强制类型转换,容易用错,也容易理解错;

b)借助super;
super.父类成员变量名

五:子类与父类构造器
初始化一个对象时,是父类的构造器先执行,子类的构造器后执行;
结论:系统在构造这个Men对象的时候,先把父类那部分构造出来,然后再来构造子类这部分,所以系统自动先调用了父类默认构造函数,然后调用子类的默认构造函数;

super(。。。。)
super()调用父类构造器则该行代码必须出现在子类构造器的第一行;否则就会报告语法错误;

结论:
a)不管是否使用super()来调用父类构造器,在初始化一个Men对象时,父类构造器和子类构造器都只会执行一次;
如果带super()调用,则调用的是父类的对应super所提供参数的构造器,否则调用的是父类的默认构造器【不带参数的】
b)既然可以通过super()调用父类构造器,可以推出通过 this()可以调用本类的其他构造器;
super(…)和this(…)不能同时出现,因为这两者都要占用子类构造器第一行的位置,你只能使用其一;
c)如果子类构造器中既没有使用super(…),也没用使用this(…),那么系统会先调用父类构造器,再调用子类构造器,这个顺序大家一定不要反;
执行构造器的顺序:从最老的到最小的——祖爷爷,爷爷,父亲,孙子

(十)package、import

一:package
package:“包”,是一种组织代码的方式;用来解决一个类名重复的问题;
A公司Time类
B公司Time类
为了区别相同的类名,Java中以入了package机制,提供一个所谓包名【名字空间/命名空间】,我们可以把一堆/一组类 放到 这个包名之下;
比如A公司的类我放到CorA这个包名之下;比如B公司的类我放到CorB这个包名之下;

总结:我们可以通过package把一组类组织到一起,让这些类归属到同一个package之下,也就是归属到这个包名之下;
如何把一个类放到一个包名之下,格式:
package 包名; //一般来讲包名都小写,整个包名是个有效的标识符即可;
写在Java源文件的最上边第一个非注释行位置;
这样该源文件中所有的类都被包含在这个包名之下,那么此时再引用这些类名的话,完整的类名就应该是 包名.类名

如果你使用了package包住了一个后者多个类,那么在文件系统中,目录组织结构也要跟包名层次相同;

几点说明:
a)域名 www.abc.com,我想我写的java放到一个包下边,包名:com.abc.www
结论:包名中带.表示的是一个目录的层次结构, .后边代表的是一个子目录。
b)因为package语句是必须要作为源码文件中的第一条非注释语句。所以一个.java源码文件中,只能指定 一个包;
也就是说,这个.java源码文件里定义的类,只能归属到一个包里,不可能再放到其他包里;
c)如果一个java源码文件不指定任何package。那么我们就可以认为这个源码文件被防止到了一个默认的package;
为了防止名字冲突,我们一般做实际工程的时候,如果工程比较大,用的第三方库内容比较多,就应该有必要把各个源码文件都指定到适当的包里边去;
d)同一个包里边的类可以自由的通过类名来直接访问。
e)不同包里的类不可以通过类名直接访问。而是要是哦那个我们前面介绍过完整类【包名.类名】的方式来引用类;

二:import
能力就是可以向某个java源码文件中导入指定的某个包下的某个类或者全部的类,那这样我们使用某个类的时候就可以直接使用类名而无需使用包名.类名这种方式;
一般import语句出现的位置是再package语句之后,类的定义之前;import可以出现多次以导入多个包下面的类;
格式1:
import 包名.类名

格式2:导入指定包下面的全部类,格式如下:
import 包名.* ,这里这个*就表示所有类;
import不是必须使用;
另外Java默认情况下会为咱们导入一些系统中提供的包下面的类;,比如String;

(2.1)import使用的困扰
import如果依旧遇到类名冲突,那么必须还是需要使用 包名.类名 的方式来使用相关类;

(2.2)import静态导入
导入某个静态成员变量/静态方法,甚至可以导入 某个类的 全部的静态成员变量和方法,看看语法,分两种:
a)导入某个类的单个的静态成员变量、方法
import static 包名.类名.静态成员变量名|静态方法; ,这里这个|表示的两者任选其一;

b)导入某个类的全部的静态成员变量、方法
import static 包名.类名.; ,这里这个代表的是静态成员变量或者方法名

(2.3)import和import静态导入的区别
常规的import,主要作用是让咱们可以免去写包名之苦,只写类名;
import static更猛,使用之后连类名都不需要写了,直接写【import进来的类的】静态成员变量名/静态方法名;
不管怎么说,这两种import都简化了程序的书写;

System.out.println(“Hello World_1!”);
a)java.lang 是个包名,这个包下面也有若干类。java默认情况下,会把java.lang这个包下的所有类都导入到所有的.java源码文件中【无需手工import】;
b)System是个类,是java.lang包名下的类,out实际上是System类下的静态成员变量,类类型【PrintStream】的静态成员变量;println是PrintStream类中的方法;

package,import还是解决一个名字冲突问题,import解决偷懒书写的问题;

(十一) 封装性与访问控制

面向对象程序设计三大特性:封装性,继承性,多态性。

一:封装性浅析
封装性:通过设置适当的访问级别来限制别人对我们的访问。
封装性的核心思想:该隐藏的隐藏,该暴露的暴露;

二:访问控制权限等级和访问控制符
Java中一共有四个访问控制权限等级,这四个访问控制权限等级 从最大权限 到 最小权限 用如下这些访问控制符[修饰符]来表示:
a)public:公共的 【公共访问权限】
b)protected:保护的【子类访问权限】
c)啥也不写:缺省的【包访问权限】
d)private:私有的【类访问权限】

定义类时的格式: [修饰符] class 类名{…}
定义成员变量时的格式:[修饰符] 类型 成员变量名;
定义方法时的格式: [修饰符] 方法返回类型 方法名(…){…}

访问控制等级表

a)public:最开放的访问控制等级 ,如果成员变量/方法/类 使用了public这个访问控制符来修饰,那么被修饰的这个成员变量/方法/类就可以被所有类访问,
不管访问别的类的类或者被访问的类是否处于同一个包中,是否有父子关系,都无所谓;
一般我们需要能够被外界访问的东西,我们就定义为pubic;所以public也被叫做公共访问权限;

b)protected:如果成员变量/方法/类 使用了protected这个访问控制符来修饰,那么就没办法做到跨包访问;
【子类访问权限】,意味着子类中能够访问;。。。。待演示

c)啥也不写(缺省的):又被称为 包访问权限,也就是说它的访问 一般被限制在一个包里;

d)private

三:访问控制的总结与实践
(1)访问控制符用来控制一个类的成员是否能够被其他类访问;
(2)虽然我们介绍了四种访问控制符,但是,我们在定义 一个类的时候,我们只会用到其中的两种:public或者 缺省;
如果该类被用public修饰,那么这个类就能够被其他所有类使用;如果这个类没有用任何修饰符来修饰,那么这个类只能被同一个包中的其他类使用;
(3)一章二节 “源文件名与类名的关系”的话题。
一个Java源文件定义的所有类(可以为多个类),的前面都没有public修饰符,那么Java源文件的文件名可以任意起,只要文件名合法即可;
但是如果你这个Java源码文件里定义了一个用public修饰的类,那么,这个源文件的主文件名必须要和这个public修饰的类同名。
所以,一个java源码文件中最多只能定义一个public类;
(4)访问控制符要解决的问题:解决类的开发者和类的使用者之间的关系问题。

四:类设计的一些指导原则
a)不需要对外使用的成员变量和方法全部用private修饰;
b)对于父类中的方法,需要在子类中进行覆盖重写等等,这种方法,在父类中习惯用protected;
c)希望暴露给其他类自由使用的,一般用public修饰。值得一说的是我们创建一个类对象时一般都会调用类的构造器,所以,构造器一般都要写成public:

《java设计模式》:设计模式要解决的问题是:当项目越来越大,文件越来越多,如何做到项目 “灵活设计,容易扩展”方面的学问;
设计模式 :涉及到很多特殊的代码写法;

d)如果一个类你也希望外部能够自由使用,那么这个类前边你就用public;不然,这个类就只能被同一个包中的其他类使用,无法做到跨包了;

(十二)多态详解

面向对象程序设计三大特性:封装性,继承性,多态性。
《设计模式》多态性随处可见,所以大家必须要掌握;

一:多态性举例简单看
父类指针【引用】指向子类对象,这个是我们探讨多态性的前提; 子类指针指向父类对象,不可以;

二:编译时类型,运行时类型
类对象,是一种引用类型变量,象一个指针一样,指向堆中的一块表示对象的内存;
那么这种引用类型变量,实际上在java中,是有两个类型,第一个 ,就是编译时类型,第二个就是运行时类型
a)编译时类型:声明对象时的类型,在编译的时候就能固定下来;你声明它时他前边跟的是啥类型,那么编译时类型就是啥;
b)运行时类型:程序运行起来,那么对象名实际指向的堆内存中的对象类型,就是对象的运行时类型;
Human myhuman2 = new Men(); myhuman2编译时类型是Human(父类),myhuman2运行时类型是Men(子类)

三:多态性的清晰解释
(3.1)多态性产生的前提条件
a)对象的编译时类型是父类类型,对象的运行时类型是子类类型
b)父类中有一个方法myfunc【多态方法】,但是子类覆盖了父类中的该同名方法,什么叫子类覆盖了父类中的同名方法,有几个意思:
b.1)父类和子类,这两个方法名字相同
b.2)参数相同
b.3)返回类型相同或者兼容 ,返回类型相同好理解;
兼容:比如说父类中的这个方法返回的是个类类型,那么子类要么返回这个类类型,要么返回这个类类型的子类型;

c)方法myfunc不可以是private,不能static静态的;
d)通过对象名.方法名(…);这种格式来调用这个方法,形如:myhuman2.myfunc();
e)…

(3.2)多态性的解释
myhuman2.myfunc(); 调用的是子类的myfunc()方法;

a)多态性体现在具有继承关系的父类和子类之间,子类覆盖了父类的方法myfunc();
b)通过父类对象名,只有到程序运行时,找到该父类对象所指向的真正堆内存中的类对象实例,然后系统内部根据堆中对象实例的类型,
找到并调用父类或者子类的myfunc()方法,
c)那么这种调用同一个方法名,会执行不同代码段 这种特征就是运行期的多态性,简称多态性;

四:对象的强制类型转换
a)二章三节:基本类型之间的转换;
b)五章九节; System.out.println(((Human)mymen).m_Age); 把子类类型对象强制转成父类类型对象;
c)把一个父类对象强制转成一个子类对象; ((Men)myhuman2).funcmen();

五:instanceof运算符
该运算符的能力,就是在你想要把一个对象强制转换成另外一种类型之前,可以用这个运算符判断一下能否成功的转成另一种类型,
如果能成功,那么你再转,如果不能成功,那么你显然就不要去转,否则就会出现运行时的异常;
总结:确认强制类型转换是否能成功,从而能够让我们写出更健壮的代码。
格式:
对象名 instanceof 类型
返回:true或者false,如果这个对象名 指向的是类名所对应的类实例,则返回true,否则返回false;

六:综合举例
大类型对象往小类型对象转换,总是可以成功;

调用一个多态方法,取决于类类型引用指向的真实类对象实例,指向的是哪个类对象实例,就执行哪个类的多态方法;

(十三)初始化块详解

一:初始化块简介
在类定义中用{}括起来的一段代码
1)一个类定义中,可以有多个初始化块。不过意义不大,因为可以合并到一个初始化块里面去;
2)初始化块的执行顺序是按照从上到下的定义顺序来执行的。

验证一个问题:构造器先执行还是初始化块先执行?
初始化块先执行,构造器后执行。
初始化块:无,他也没有办法被调用,只能在new对象时自动执行。所以感觉上初始化块干了构造器的事;

类中的这种成员变量的定义并初始化代码和 初始化块的执行是平级的。
也就是说:在类定义中,谁排前边就先执行谁。谁排在后边就后执行谁。

new一个对象时代码的执行顺序:
1)成员变量的定义并初始化代码 和 初始化块 ,谁排在前面先执行谁。两者都会执行完毕。
2)执行构造器。
初始化块到底用不用,取决于个人习惯。

二:继承关系中初始化块执行顺序
5章9节
new子类对象时:a)父类初始化块执行,b)父类构造器执行, c)子类初始化块, d)子类构造器执行。
new子类对象时,如果父类中有初始化块,该初始化块也会被执行。

三:静态初始化块/类初始化块
静态初始化块先执行(多个静态初始化块则谁先定义【排在上面】谁先执行),然后是 普通初始化块,最后才是 构造器的执行。

new子类对象时: a)父类的静态初始化块执行, b)子类的静态初始化块执行。 c)父类初始化块执行,d)父类构造器执行, e)子类初始化块, f)子类构造器执行。
能够发现,最优先执行的是父类静态初始化块,紧接着子类的静态初始化块执行,然后其他的顺序不变。

static(静态的),他所修饰的内容,是属于类本身【跟着类走的】,而不属于该类的摸个对象。所以静态初始化块 也成为 类初始化块;
(1)对类进行初始化时才执行类初始化块。 问题是:什么时候对类进行初始化?
答案就是:只要你碰触到这个类,系统就会对类进行初始化,而且仅仅初始化一次。
也就是说,类初始化块在整个程序执行过程中,仅仅会被执行一次 ****************;
a)碰触到这个类,系统就会对这个类初始化;
b)对类初始化时,就会执行类的初始化块;

类初始化块的执行顺序是:辈分越高(越老)越先执行;

类初始化块在整个程序执行过程中,仅仅会被执行一次;
普通初始化块,对象创建多少次,普通初始化块就执行多少次;

(2)类初始化块中能够执行的内容:因为类初始化块是跟着类走的,所以能引用的只能是静态成员变量和静态方法,不能引用普通成员变量和普通方法;
(3)类初始化块与静态成员变量初始化的执行顺序问题:
谁排在前面先执行谁,谁排在后边后执行谁;

四:不要在父类构造器中调用被子类覆盖的方法

(十四)抽象类与抽象方法

一:抽象类的概念
1:抽象类——类
2:抽象:抽出,概括出 他们共同的方面、本质属性与关系
“把一些共通的东西提取出来”

抽象类一般用来做父类:把一些公共的东西抽象到父亲中来;
抽象类:只需要在类定义前面(class前面)增加abstract这个修饰符即可;

二:抽象方法的概念
普通方法如何变成抽象方法,需要两步:
a)把方法定义前面加上abstract这个修饰符.
b)方法要全部干掉,方法后面要增加分号;
理解:可以认为抽象方法是 一种不完整的方法:只有声明,没有方法体。

三:抽象类和抽象方法的一些特性
a)都用abstract来修饰,同时抽象方法不能有方法体;
b)抽象类不能用new来实例化(初始化),比如Human myhuman = new Human();是非法的,不允许的;
c)抽象类中不一定非要有抽象方法,只要类定义的时候增加了abstract修饰符,那么这个类就是个抽象类。
d)抽象类中也可以有普通成员变量,普通方法,初始化块,也可以 有静态成员变量,静态方法,静态初始化块以及自己的构造器。
因为抽象类没有办法实例化,所以抽象类中的构造器其实主要是给子类用的;
e)如果一个类里面要有抽象方法,那么这个类必须得是抽象类。否则编译报错;
所以大家要记住:普通类里面不能包含抽象方法;

四:抽象类的使用
抽象类用于被当做父类,供子类来继承。子类继承父类后,必须要把父类中的抽象方法实现掉;
在子类中,实现了父类的抽象方法,而且,子类也不再是抽象类;
总结:
a)抽象类,用于被继承(当爹)。
b)抽象方法:必须要在子类中重写——在子类中要为该方法写方法体,变成一个真正的方法;
c)抽象方法,只能在普通方法之前增加abstract,末尾增加;,然后去掉方法体,得到了抽象方法;
静态方法,构造器,前面不能增加abstract;
d)另外,因为子类要覆盖父类的抽象方法,所以,父类中的抽象方法,不可以设置为private,因为private权限是类访问权限,只能限制在本类,父子关系类是不行的;

五:抽象类引入的目的探讨
a)抽象类不能创建实例,但可以被当做父类,用来创建子类。然后可以把一些公共的接口写成抽象 方法,弄到抽象类中去,然后所继承的子类重写这些抽象方法。
这种做法就好像父类给子类提供给了一些书写规范(抽象方法),然后子类遵照这个书写规范来实现这些抽象方法。
b)用抽象父类的一个好处,就是抽象方法的方法体不用写。推迟到子类中去写方法的具体实现。
c)如果在父类中不写抽象方法,是不是更好呢?当然不是,原因有二:
c.1)抽象类是给子类指定一个书写规范(书写模板),要求子类必须重写父类的抽象方法。所以抽象方法起到规范、表率作用。所以,抽象方法不应该删掉。
c.2)为了多态考虑(父类对象指向子类实例);保证 myhuman2.myabsfunc();写法能够成功调用,所以该抽象方法必须要写在父类中(抽象方法)。

(十五)final用法详解

final:修饰符/关键字 ,可以修饰
成员变量(普通成员变量/静态成员变量), 局部变量(形参)
方法

大概意思:最终的,最后的。 不能改变;

一:final修饰的变量
final修饰的变量,特性是:一旦有了初值之后,值就不能够被改变了;

(1.1)成员变量
普通成员变量给初值:
a)定义的时候给初值
b)在初始化块中给初值
c)在构造器中给初值
d)如果创建了对象,可以在外部 用对象名.成员变量名 给值
如果不给初值,那么成员变量也有缺省值;比如整型成员变量,缺省值是0.boolean=false,Human=null;

一旦成员变量用final修饰,那么就必须得给这个成员变量指定一个初始值。否则就会报语法错误。
那么可以在哪里给这个final成员变量初始值呢?
final修饰的普通成员变量:
a)可以在定义的时候给初始值
b)在初始化块里给初始值
c)构造器里也可以给初始值
三个地方,只要有一个地方给值,其他两个地方就不要给值;

final修饰的静态成员变量:
a)可以在定义的时候给初始值
b)在静态初始化块里给初始值
二个地方,只要有一个地方给值,另外一个地方就不要给值;

(1.2)局部变量
final修饰的普通局部变量
可以在定义的时候给初值,也可以在定义的时候不给初值,后续用赋值语句给值。但是一旦有了值就不可以再给其赋值了;

final修饰的方法中的形参
形参的值是通过调用该方法传递的实参值来给的;

final修饰的引用类型的局部变量
用final修饰的引用类型局部变量,一旦指向了某块堆内存,就不允许再指向其他的堆内存;(指向的位置不允许改变,但指向的内容可以改变)

(1.3)final的宏替换能力

为什么两个字符串【String】内容完全相同,但是比较的结果却不相等
a)有些字符串的内容/值在编译的时候就能确定,比如s1,s2,sr,sresult,sresult2
而sresult3,编译的时候确定不了其值,而需要在运行的时候才能确定sresult3的值。
b)Java针对针对字符串等,有一些实现技巧在里边,比如,对于这种编译期间就能确定字符串内容的,这些内容会被缓存到一个专门的地方去;
String sr = “abc” + “def”; //编译时就能确定sr值

如果一个变量用final修饰,并且在定义的时候就指定初值,那么,这个变量的值在编译的时候就能确定下来;
宏替换是在编译的时候替换;

二:final修饰的方法
一旦一个方法被final修饰,那么这个方法就不可以被子类覆盖了。所以父类中的不希望被子类覆盖的方法,可以用final修饰;
如果子类强行覆盖,会产生编译错误;
private权限:只能在本类中访问的一个权限;
private final不要联合起来修饰一个方法,没有任何意义;

三:final修饰的类
final修饰的类,不可以被继承;也就是说,该类不能有子类

(十六) 接口详解

一:接口的基本概念和创建
接口:比抽象类还抽象,或者理解成100%的纯抽象类。 抽象类里的概念多数都适合接口。
在这里:就把接口理解成抽象类。—接口 就理解成 类

接口格式:
[修饰符] interface 接口名
{
}
修饰符:一般就是public,不加的话就是缺省访问权限(包访问权限),参考11节

二:接口的使用
接口也不能被实例化,比如说new一个接口,不可以;
接口内部一般都定义一些规范性的东西。规定 继承者(规范的实现者)必须要实现的东西。
所以, 一般都是用于 用具体的类(实现类)去实现这个接口(接口是用来当爹)。实现这个接口的类,叫做实现类。

注意:继承用的extends关键字,实现一个接口用的是implements,所以:
a)一个类只能继承自一个父类,但是一个实现类可以继承【实现】自多个接口(接口本身也可以继承多个其他接口)
b)一个类可以实现多个接口,这也算是弥补了一个类只能继承自一个父类的遗憾,这个也叫“模拟多继承”
c)而且extends关键字和implements关键字 意思差不多。都是从长辈(前辈)那里继承下来东西----成员变量、方法
d)当extends和implements关键字同时出现在一行,extends在前面,implements在后面。

实现一个接口跟继承一个父类感觉差不多。接口中的很多内容在实现类中就有了;
虽然子类不可以继承多个父类,但是实现类可以实现多个接口。

用实现类实例化对象,多态:父类型指向子实例
a)接口不能被实例化(不能出现在new的后面);这一点跟抽象类一样;
kxinterface mi = new kxinterface(); //非法
b)接口可以被用于声明【定义】引用类型变量:多态;
kxinterface mhi3 = new impint();
但是new的时候【动态类型】,必须new一个具体的类。而不能new一个接口;

三:接口核心思想和可以放入其中的内容
接口一般定义一些规范性的东西,但是不提供具体的实现。 规范和实现分类的设计原则(接口的优点);
规范:接口中
实现:在具体的实现类中

 主板中的插槽(接口),
 网卡,声卡,显卡(实现)。

(3.1)接口中不允许出现的内容
a)构造器
b)初始化块
c) 接口可以继承自多个接口(后面演示),但是不可以继承类
d)接口中不能出现普通成员变量(只有静态成员变量),没有普通方法(有方法体的方法),只有抽象方法(没有方法体)。
(3.2)共性问题
接口里面大部分内容都是public访问权限的。所以,在接口中定义的内容可以不写public,但是要写,必须要写成public,写成其他修饰符会报语法错;
(3.3)接口允许出现的内容

1:成员变量(静态成员变量)
因为接口中没有构造器,没有初始化块,所以定义成员变量时就得给初值。
在接口中出现的成员变量,系统会自动为他加上static和final。
static:跟着类走的
final:必须给这个成员变量指定初始值,而且一旦有初值就不能修改
所以,接口中的成员变量实际上是一个静态成员变量

2:普通方法(抽象方法)
在接口中出现的普通方法,其实都是抽象方法,也就是说,不可以 有方法体。
在实现类中必须要实现该方法

3:静态方法(类方法)
跟着类走的,用static修饰。必须有方法体,否则报错;
只能通过接口名调用

4:默认方法(实例方法)
用default来修饰 ,public可以省略。必须要有方法体,否则报错。
跟着对象走的,所以不可以和static连用;

与普通方法的差别是,默认方法带方法体,而普通方法不带方法体。
默认方法弥补普通方法中无法写方法体的缺憾。

5:私有方法(工具方法)
用private来修饰。比如有方法体,否则报错;
a)跟着对象走的(普通的私有方法)
b)跟着类走的(静态私有方法)
私有方法,在main等函数中是无法调用的;私有方法的用途是什么呢?
a)不暴露给外界,也就是说是隐藏的,仅供接口内部调用。所以也称为工具方法——实现一些公共代码。
b)又能够被内部的各种方法调用,比如能够被 静态方法,其他私有方法,默认方法。。。。但是不能被普通方法调用(因为普通方法在接口中根本没方法体);

四:接口的继承(继承的关键字用extends)
接口支持继承,而且支持多重继承。一个接口可以有多个父亲,但是接口只能继承自接口,不能继承类。

五:接口与抽象类的概念概括、接口与抽象类的异同
(5.1)接口与抽象类的概念概括
a)抽象类是从多个类中抽取出来的模板,样板。接口好像是100%的纯抽象类。或者说把抽象进行的更彻底,进一步提炼生成这种更加特殊的抽象类–接口。
b)接口是一种设计规范,具体的实现是通过继承这个接口的实现类来实现的,所以,接口体现的是一种规范和实现分离的逻辑。
c)接口,可以看成是 一个类,只不过这个类比较特殊。
d)接口的主要用途还是被当做具体实现类的父亲。并在具体的实现类中实现接口中需要实现的东西,比如接口中的普通(抽象方法);

(5.2)接口与抽象类的异同
a)两者都不能被实例化,不如new一个接口,new一个抽象类都不行。
b)抽象类一般通过子类的继承extends来使用。
接口通过一个类的实现implements来使用。其实 实现 理解成 继承也没问题;所以抽象类和接口,都是等着别人来继承(高辈分);
所以,如果想实现多继承的效果,使用接口,因为类不支持多继承;
c)有些内容接口里可以有,抽象类里不可以有。
有些东西在抽象类里可以有,在接口里不可以有。
接口里能包含啥呢?

规律:
c.1)接口类中不可以包含的内容,抽象类中都可以包含
c.2)接口中的独有内容,默认方法,用default修饰的,抽象类中没有default修饰的方法;
c.3)接口中出现的很多东西,都是自带public修饰和static修饰
c.4)随着java的升级,在抽象类,接口中,还会引入新语法,新内容。遇到再说;

d) 接口和抽象类各有 用途;
d.1)单从语言来讲,接口和抽象类界限越来越模糊
d.2)若从Java语言设计者设计初衷来讲:
抽象类,子类:父子关系; 从子类中能够看到父类的身影。
接口,实现类:不是一种父子关系。 接口和 声卡、网卡、显卡之间没有父子关系,只是一个抽象和一个具体的关系。
抽象:把一些公共的东西抽取出来。 具体实现,就是声卡发声,网卡要传输数据,显卡显示图像;

所以,接口比抽象更狠,叫做100%的纯抽象类(极度抽象)。从实现类中完全无法看到接口的全貌
   所以,接口,对实现类的限制也就更少,更宽松。编写代码的时候可复用性就更好。

e)是写一个抽象类还是写一个接口
优先考虑接口,如果不合适,再考虑抽象类。
切记:有必要的时候才去创建接口或者抽象类。如果是一个具体的类就能搞定的事情,那么就完全不需要抽象类或者接口。
项目越小,越不需要抽象类或者接口。
坏处:增加程序复杂性,增加别人理解难度;
好处:增加了灵活性。
在使用和滥用之间,大家必须要把握一个度。
设计模式的程序员;

(十七)内部类_1

一:概念和基本使用
内部类(嵌套类)
外部类/宿主类
内部类/嵌套类
(1.1)内部类的基本使用
(1.2)内部类访问外部类的成员
类:方法、构造器、成员变量、初始化块 、 内部类(也看成是类中一员)

a)外部类能够访问内部类的私有成员变量
b)内部类也可以访问外部类的私有方法、私有成员变量

(1.3)变量访问顺序探讨
局部变量优先,本类中的成员变量(this.成员变量名),外部类的成员变量(外部类名.this.成员变量名)。

内部类的方法可以直接访问外部类的成员变量。但外部类的方法不能够直接访问内部类的成员变量。除非new一个内部类对象,
然后通过new的这个内部类对象去访问内部类的成员变量。

********为什么内部类可以直接使用外部类的成员变量,而外部类却不能直接使用内部类的成员变量。
成员变量跟着对象走,有对象才有成员变量:
a)能调用内部类的普通方法, 一定有一个 内部类对象(hf)存在
b)如果有一个内部类对象存在,则一定会有个外部类对象(myhuman)存在。
a,b)综合:有一个内部类对象存在,则一定会有一个外部类对象存在,所以,在内部类普通方法中,能够直接访问外部类对象的成员变量。
(内部类对象隐式的保存了指向创建他的外部类对象的 箭头/引用/指针);
c)相反,有外部类对象存在,不代表有内部类对象存在,所以,外部类的方法中不能直接使用内部类的成员变量。

(1.4)静态规则
a)不允许在外部类的静态方法中使用内部类;
b)内部类不要沾染上和 “静态(static)”关键字有关的内容,否则就会报语法错

二:内部类种类
(2.1)普通内部类:类内部定义另外一个类。外部类访问普通内部类看起来和访问一个普通类区别并不太大。
(2.2)局部内部类:作用域局限在定义他的方法中,无法在外部类的其他方法中使用。感觉用处相对比较小。
在任何一个作用域内都可以嵌入一个局部内部类。
(2.3)静态内部类:用static修饰的类就是静态内部类。
静态,跟着类走的,跟对象没有关系;
一旦定义一个静态内部类,那么该类就属于外部类的组成部分了(跟外部类对象没有什么关系)。
a)基本上,什么成员都能够往静态内部类里面放。
静态内部类和外部类之间访问数据的时候,把握一些原则:
(2.3.1)内部类的静态方法中不要去访问那些外部类非静态的成员变量。

(2.3.2)静态内部类中的普通方法也不要去访问外部类的普通方法,只能访问外部类的静态方法。这点大家要专门记一下。

静态内部类和外部类之间的一些调用关系;
以往:类中 静态方法,静态成员变量,静态初始化块,现在有多了一个静态类(当成外部类的静态成员)。
a)外部类借助静态内部类的类名;
b)外部类借助静态内部类对象;

(2.4)接口中的内部类
在接口中,是可以在一个内部类中定义一个静态成员变量的,为什么?
因为在接口中,会自动为内部类使用static修饰,也就是说,接口中出现的内部类,都会是静态内部类。

(十八)内部类_2

一:概念和基本使用
内部类(嵌套类)
外部类/宿主类
内部类/嵌套类
(1.1)内部类的基本使用
(1.2)内部类访问外部类的成员
类:方法、构造器、成员变量、初始化块 、 内部类(也看成是类中一员)

a)外部类能够访问内部类的私有成员变量
b)内部类也可以访问外部类的私有方法、私有成员变量

(1.3)变量访问顺序探讨
局部变量优先,本类中的成员变量(this.成员变量名),外部类的成员变量(外部类名.this.成员变量名)。

内部类的方法可以直接访问外部类的成员变量。但外部类的方法不能够直接访问内部类的成员变量。除非new一个内部类对象,
然后通过new的这个内部类对象去访问内部类的成员变量。

********为什么内部类可以直接使用外部类的成员变量,而外部类却不能直接使用内部类的成员变量。
成员变量跟着对象走,有对象才有成员变量:
a)能调用内部类的普通方法, 一定有一个 内部类对象(hf)存在
b)如果有一个内部类对象存在,则一定会有个外部类对象(myhuman)存在。
a,b)综合:有一个内部类对象存在,则一定会有一个外部类对象存在,所以,在内部类普通方法中,能够直接访问外部类对象的成员变量。
(内部类对象隐式的保存了指向创建他的外部类对象的 箭头/引用/指针);
c)相反,有外部类对象存在,不代表有内部类对象存在,所以,外部类的方法中不能直接使用内部类的成员变量。

(1.4)静态规则
a)不允许在外部类的静态方法中使用内部类;
b)内部类不要沾染上和 “静态(static)”关键字有关的内容,否则就会报语法错

二:内部类种类
(2.1)普通内部类:类内部定义另外一个类。外部类访问普通内部类看起来和访问一个普通类区别并不太大。
(2.2)局部内部类:作用域局限在定义他的方法中,无法在外部类的其他方法中使用。感觉用处相对比较小。
在任何一个作用域内都可以嵌入一个局部内部类。
(2.3)静态内部类:用static修饰的类就是静态内部类。
静态,跟着类走的,跟对象没有关系;
一旦定义一个静态内部类,那么该类就属于外部类的组成部分了(跟外部类对象没有什么关系)。
a)基本上,什么成员都能够往静态内部类里面放。
静态内部类和外部类之间访问数据的时候,把握一些原则:
(2.3.1)内部类的静态方法中不要去访问那些外部类非静态的成员变量。
(2.3.2)静态内部类中的普通方法也不要去访问外部类的普通方法,只能访问外部类的静态方法。这点大家要专门记一下。

静态内部类和外部类之间的一些调用关系;
以往:类中 静态方法,静态成员变量,静态初始化块,现在有多了一个静态类(当成外部类的静态成员)。
a)外部类借助静态内部类的类名;
b)外部类借助静态内部类对象;

(2.4)接口中的内部类
在接口中,是可以在一个内部类中定义一个静态成员变量的,为什么?
因为在接口中,会自动为内部类使用static修饰,也就是说,接口中出现的内部类,都会是静态内部类。

(2.5)匿名内部类
a)匿名内部类必须有一个爹。这个爹可以是个类(),也可以是个接口。
new 接口名(){…} 或者 new类名(参数){…}
b)匿名类属于一种一次使用的类,创建这个类的时候系统就自动创建了一个该类的“实例”,该类不能复用;
c)匿名类没有名字,所以没有自己的构造器,但是可以有初始化块。
d)定义匿名类不需要使用class关键字;这种new的写法类似于:
class 匿名类 implements kxinterface{…}

表面上叫匿名内部类,实际上他真正代表的是该类的实例,所以说它可以当参数用。甚至,在一个方法中,直接 return kxinterface()…都可以;
匿名内部类,一般只能是覆盖一下父类中一些已经有的方法接口才有实际意义。
匿名内部类引入的目的是简化程序书写。否则可能就要单独创建一个类,并且定义并初始化该类的对象,然后把对象当做参数传递给其他方法。

三:内部类的使用
(3.1)在外部类的里面使用内部类
和使用一个其他的普通类没有太大区别,一般就是new一个内部类的对象。然后调用内部类的方法。

(3.2)在外部类的外面使用普通内部类
格式: 外部类名.内部类名 变量名;
Human.HumanFood myfood; //定义一个变量
Human.HumanFood myfood = new Human.HumanFood(); //语法错误的原因,前面说过,有内部类对象在则一定要有一个外部类对象在。

(3.3)内部类的访问权限
外部类修饰:public或者啥也不写
内部类:public,啥也不写,protected,private。
a)public:公共,被任意访问。
b)protected:保护的,不能跨包,可以被与外部类同一个包中的其他类以及外部类的子类访问。
c)啥也不写:跟protected几乎一样:不能跨包,可以被与外部类同一个包中的其他类以及外部类的子类访问。
d)private :私有的,只能在外部类的内部使用。

(3.4)在外部类的外面使用静态内部类
静态内部类是跟着外部类走的,所以创建静态内部类对象并不需要创建外部类对象。
.class字节码文件。
每个内部类都产生一个.class文件,命名规则一般都是 :外部类名 内 部 类 名 . c l a s s 局 部 内 部 类 , 在 内部类名.class 局部内部类,在 .class后还会带一个数字。
匿名内部类的命名方法,外部类名$数字

(3.5)内部类的继承:特殊写法,见具体实例: myf.super(“good”);

(4)内部类的用途简述
(1)内部类允许把一些逻辑相关的类组织到一起;
(2)能够与外部类之间互相访问(通信)。外部类有一些方法可以被内部类访问,就相当于内部类有了这些访问接口。
(3)可以设置内部类的各种访问权限。

(十九)lambda表达式_1

一:lambda表达式的引入
上节课:匿名内部类
lambda表达式能够简化 这种 创建匿名内部类的代码————lambda表达式的能力——简化代码;
lambda表达式和匿名内部类写法的不同点:
a)new InfAdd()这行没了;
b)看起来抽象方法名myfuncadd也不用写了;
c)返回值类型也不用写了
d)形参有,但是形参的类型也不用写了
e)都是使用{}将实现体或者方法体括起来
所以,lambda表达式理解成一个 “匿名方法”

二:lambda表达式的组成
(形参列表)->{代码块}
a)形参列表中的形参类型,可以省略。而且如果形参只有1个,那么形参列表中的()也可以省略。
如果没有形参,那么就只保留一个()
b)如果代码块只有一行代码,那么代码块外面的大括号也可以省略:
末尾的分号要去掉;如果lambda是 一条return语句,则return也不要写了。lambda表达式能够自动把->右面的内容的值返回去;

三:变量捕获
(3.1)对final局部变量的捕获
a)匿名类/lambda都能够访问局部变量(访问的必须是final特性的局部变量)
b)局部变量必须具有final特性。如果有赋值语句存在abc = 250;,那么这个变量abc就不可能具备final特性——因为final类型的局部变量是不可以改变值。

(3.2)对外部类静态成员变量的捕获,能够成功捕获
(3.3)对外部类成员变量的捕获

四:函数式接口
能够 用lambda表达式来取代匿名内部类,是有条件的,并不是什么时候都能取代的;
(1)myUserN中形参的类型InfAdd必须是一个接口
如果想用lambda表达式来取代匿名内部类,则匿名内部类的爹必须是接口,不可以是一个类;
(2)对接口的要求:接口中只允许有一个抽象方法;
从而引出 “函数式接口”,接口中可以有很多内容,但是 对于抽象方法,接口中只允许有一个———包含一个抽象方法的接口就叫函数式接口。
在创建匿名内部类的时候,如果选择函数式接口当爹,那么这种匿名类,就可以用lambda表达式替换,
毫无疑问,lambda表达式里面的代码块其实就代表着 函数式接口中 唯一的 那个抽象方法的方法体。
所以,你用lambda表达式取代匿名内部类的时候,也不需要指定抽象方法名,因为接口中就唯一这么一个抽象方法。
(3)lambda表达式的类型,是属于一种不受限制的任意类型:形参是什么类型,我lambda就是什么类型,也就是说,lambda表达式的实参类型取决于形参类型。
只要形参的类型是函数式接口就可以。
(4)系统中有很多的内置的函数接口,这些接口中的抽象方法名各不相同,当然实现的功能也各不相同,以后遇到再说;

(二十)lambda表达式_2

一:lambda表达式的引入
上节课:匿名内部类
lambda表达式能够简化 这种 创建匿名内部类的代码————lambda表达式的能力——简化代码;
lambda表达式和匿名内部类写法的不同点:
a)new InfAdd()这行没了;
b)看起来抽象方法名myfuncadd也不用写了;
c)返回值类型也不用写了
d)形参有,但是形参的类型也不用写了
e)都是使用{}将实现体或者方法体括起来
所以,lambda表达式理解成一个 “匿名方法”

二:lambda表达式的组成
(形参列表)->{代码块}
a)形参列表中的形参类型,可以省略。而且如果形参只有1个,那么形参列表中的()也可以省略。
如果没有形参,那么就只保留一个()
b)如果代码块只有一行代码,那么代码块外面的大括号也可以省略:
末尾的分号要去掉;如果lambda是 一条return语句,则return也不要写了。lambda表达式能够自动把->右面的内容的值返回去;

三:变量捕获
(3.1)对final局部变量的捕获
a)匿名类/lambda都能够访问局部变量(访问的必须是final特性的局部变量)
b)局部变量必须具有final特性。如果有赋值语句存在abc = 250;,那么这个变量abc就不可能具备final特性——因为final类型的局部变量是不可以改变值。

(3.2)对外部类静态成员变量的捕获,能够成功捕获
(3.3)对外部类成员变量的捕获

四:函数式接口
能够 用lambda表达式来取代匿名内部类,是有条件的,并不是什么时候都能取代的;
(1)myUserN中形参的类型InfAdd必须是一个接口
如果想用lambda表达式来取代匿名内部类,则匿名内部类的爹必须是接口,不可以是一个类;
(2)对接口的要求:接口中只允许有一个抽象方法;
从而引出 “函数式接口”,接口中可以有很多内容,但是 对于抽象方法,接口中只允许有一个———包含一个抽象方法的接口就叫函数式接口。
在创建匿名内部类的时候,如果选择函数式接口当爹,那么这种匿名类,就可以用lambda表达式替换,
毫无疑问,lambda表达式里面的代码块其实就代表着 函数式接口中 唯一的 那个抽象方法的方法体。
所以,你用lambda表达式取代匿名内部类的时候,也不需要指定抽象方法名,因为接口中就唯一这么一个抽象方法。
(3)lambda表达式的类型,是属于一种不受限制的任意类型:形参是什么类型,我lambda就是什么类型,也就是说,lambda表达式的实参类型取决于形参类型。
只要形参的类型是函数式接口就可以。
(4)系统中有很多的内置的函数接口,这些接口中的抽象方法名各不相同,当然实现的功能也各不相同,以后遇到再说;

五:lambda表达式的返回结果(类型)
(1)定义了一个类,继承自InfAdd接口。并且实现了接口中唯一的抽象方法。所以,把lambda表达式理解成实现的是一个匿名方法也可以。
(2)定义了一个属于该类的对象,调用了抽象方法在类中类中的实现体;
(3)lambda表达式的类型(返回类型),也有人称为目标类型——InfAdd,其实可以是任意 一个函数式接口类型,也就是说这个目标类型是可以变化的。
但是接口中抽象方法的形参类型要一致;
lambda只能为函数式接口创建对象。

lambda表达式从本质上来说就3点:
(1)创建了一个函数式接口的实例(对象),并且实现了接口中唯一的抽象方法;
(2)语法简洁,有效的取代匿名内部类这种繁琐的语法;
(3)lambda表达式的类型是可变的,类型是由上下文推导而来。

lambda表达式的几种形式:
(1)没参数的时候:
()->{…};
(2)有超过1个参数的时候:
(参数1,参数2…)->{…};
(3)有1个参数的时候:
参数1->{…};
(4)如果lambda表达式中只有一行语句:
…->语句;

六:lambda表达式的方法引用
(6.1)引用静态方法
把Women::WomenDouble;写法看成是value->Women.WomenDouble(value);的简写形式
引用格式: 类名::静态方法名
引出 “方法引用”这个概念:不同的方法名称可以描述同一个方法。

(6.2)引用固定对象的普通方法
把wobj::PTFunc;写法看成是value->wobj.PTFunc(value);的简写形式

(6.3)引用非固定对象的普通方法
把Women::PTFunc;写法看成是(a,b)->a.PTFunc(b);的简写形式

(6.4)引用构造器
引用了构造器,肯定是用来创建对象的;因为只有创建对象的时候才会去调用构造器;
把Women::new;看成是(a)->new Women(a);的简写形式

具体怎么用,大家以后在项目中或者是通过读别人的代码来体会具体的用法;

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值