说明:此文章收录个人在JavaSE学习过程中的一些基础知识,以及一些自己爱忘记的知识点。
JavaSE(Java Standard Edition):标准版,定位在个人计算机上的应用。这个版本是Java平台的核心,它提供了非常丰富的API来开发一般个人计算机上的应用程序,包括用户界面接口AWT及Swing,网络功能与国际化、图像处理能力以及输入输出支持等。在上世纪90年代末互联网上大放异彩的Applet也属于这个版本。Applet后来为Flash取代,Flash即将被HTML5取代。
JavaEE(Java Enterprise Edition):企业版,定位在服务器端的应用。
JavaEE是JavaSE的扩展,增加了用于服务器开发的类库。如:JDBC是让程序员能直接在Java内使用的SQL的语法来访问数据库内的数据;Servlet能够延伸服务器的功能,通过请求-响应的模式来处理客户端的请求;JSP是一种可以将Java程序代码内嵌在网页内的技术;
JavaME(Java Micro Edition):微型版,定位在消费性电子产品的应用上
jdk>jre>jvm
下载jdk:点击下载
设置Path环境变量
•设置JAVA_HOME(解压jdk的目录)
•设置CLASSPATH(.;%JAVA_HOME%\lib\dt.jar;%JAVA_HOME%\lib\tools.jar)
•在path中加入两个新的配置
%JAVA_HOME%\bin
%JAVA_HOME%\jre\bin
详细方法点这里
下载jdk(Linux版本):点击下载
解压缩到指定目录(以jdk-8u191-linux-x64.tar.gz为例)
创建目录:
sudo mkdir /usr/lib/jvm
sudo tar -zxvf jdk-7u60-linux-x64.gz -C /usr/lib/jvm
sudo vi ~/.bashrc
#set oracle jdk environment
export JAVA_HOME=/usr/lib/jvm/jdk1.8.0_191 ## 这里要注意目录要换成自己解压的jdk 目录
export JRE_HOME=${JAVA_HOME}/jre
export CLASSPATH=.:${JAVA_HOME}/lib:${JRE_HOME}/lib
export PATH=${JAVA_HOME}/bin:$PATH
source ~/.bashrc
sudo update-alternatives --install /usr/bin/java java /usr/lib/jvm/jdk1.8.0_191/bin/java 300
java -version
包名:多单词组成时所有字母都小写:xxxyyyzzz
类名、接口名:多单词组成时,所有单词的首字母大写:XxxYyyZzz
变量名、方法名:多单词组成时,第一个单词首字母小写,第二个 单词开始每个单词首字母大写:xxxYyyZzz
常量名:所有字母都大写。多单词时每个单词用下划线连接:XXX_YYY_ZZZ
变量本质上就是代表一个”可操作的存储空间”,空间位置是确定的,但是里面放置什么值不确定。我们可通过变量名来访问“对应的存储空间”,从而操纵这个“存储空间”存储的值。
2局部变量、成员变量、静态变量的区别
类型 | 声明位置 | 从属于 | 生命周期 |
---|---|---|---|
局部变量 | 方法或语句块内部 | 方法/语句块 | 从声明位置开始,直到方法或语句块执行完毕,局部变量消失 |
成员变量(实例变量) | 类内部,方法外部 | 对象 | 对象创建,成员变量也跟着创建。对象消失,成员变量也跟着消失; |
静态变量(类变量) | 类内部,static修饰 | 类 | 类被加载,静态变量就有效;类被卸载,静态变量消失。 |
类型 | 占用存储空间 |
---|---|
byte | 1字节 //1byte(字节)=8bit(位) |
short | 2字节 |
int | 4字节 |
long | 8字节 |
浮点型:float(4字节), double(8字节)
float类型赋值时需要添加后缀F/f
char 型数据用来表示通常意义上“字符”(2字节)
boolean 类型适于逻辑运算,一般用于程序流程控制
不可以0或非 0 的整数替代false和true,这点和C语言不同
字符串: String类,String类属于引用类型,可用null赋值
练习题:
String str1 = 4; //判断对错:错 需要加上双引号
String str2 = 3.5f + “”; //判断str2对错:对
System.out.println(str2); //输出:3.5
System.out .println(3+4+“Hello!”); //输出:7Hello!
System.out.println(“Hello!”+3+4); //输出:Hello!34 *讲解(“”+任何内容)会将使全部变为字符串,
System.out.println(‘a’+1+“Hello!”); //输出:98Hello!*讲解(任何内容+“”)前面的正常运算,“”后面的内容变成字符串
System.out.println(“Hello!”+‘a’+1); //输出:Hello!a1
自动转换(小>>大):(byte,short,char)>>> int >>> long <<< float <<
强制转换(大>>小):
int i=2;
short a=(short)(i+1);(前面添加需要转换的类型)
.....
i+=2 同义为i= i+2
short s = 3;
s=s+2; ① //此时自动默认为int类型,short>>int为强制类型转换,报错,需要加(short)才行;
s+=2; ②//然而在使用扩展赋值运算符时,变量在参与运算时会把结果自动强制转换为当前变量的类型,可以直接运算
①和②有什么区别?
.....
int i=0;
int k=i++;
System.out.println(k);
System.out.println(i);
输出k=0,i=1
.....
int i=0;
int k=++i;
System.out.println(k);
System.out.println(i);
输出k=1,i=1
//++和--分别是加1和减1的运算,++或者--符号在变量之前,先对变量进行运算然后再取变量的值;
//如果++或者--符号在变量之后,就先取变量的值,再对变量进行运算
int x = 1;
int y = 1;
if(x++==2 & ++y==2){ //x++现在被看为一个整体b,此时,b应该先取值,值为1;再输出x运算为2
x =7;
}
System.out.println(x,y);
输出:2,2
//**此类问题若输出i,i的值一定是运算了的,而如果输出的是整体i++或者++1,就要按情况考虑先取值还是先运算。
&—逻辑与 | —逻辑或 !—逻辑非
&& —短路与 || —短路或 ^ —逻辑异或
a | b | a&b | a|b | !a | a^b | a&&b | a||b |
---|---|---|---|---|---|---|---|
true | true | true | true | false | false | true | true |
true | false | false | true | false | true | false | true |
false | true | false | true | true | true | false | true |
false | false | false | false | true | false | false | false |
逻辑运算符用于连接布尔型表达式,在Java中不可以写成3
“&”和“&&”的区别:
单&时,左边无论真假,右边都进行运算;
双&时,如果左边为真,右边参与运算,如果左边为假,那么右边不参与运算。
“|”和“||”的区别同理,||表示:当左边为真,右边不参与运算。
在不需要逻辑运算两边都参与运算的时候,尽量使用&&和||
异或( ^ )与或( | )的不同之处是:当左右都为true时,结果为false。
理解:异或,追求的是“异”!
..1..
int x = 1;
int y=1;
if(x++==2 & ++y==2){
x =7;
}
System.out.println("x="+x+",y="+y);//输出2 2
..2..
int x = 1,y = 1;
if(x++==2 && ++y==2){
x =7;
}
System.out.println("x="+x+",y="+y);//输出2 1
直接戳这里查看哔哩哔哩求知讲堂Java课程
(条件表达式)? 表达式1:表达式2;
条件表达式为true,运算后的结果是表达式1;
条件表达为false,式运算后的结果是表达式2;
.....
int i=2,b=3;
System.out.println((i>b)? i:b);
输出:3
....
int a=0,b=1,c=3;
System.out.println((a>b)?((b>c)?a:c):((a<c)?c:b));
//输出:3
package pkg1.pkg2.pkg3; //路径包含关系:pkg1>pkg2>pkg3
一般利用公司域名倒置作为包名:如com.baidu.www //com文件夹包含baidu文件夹包含www文件夹
import package1[.package2...].(classname);
import java.util.Scanner //导用scanner包
public class Demo01{
public static void main(String[] args){
Scanner a=new Scanner(System.in);
//输出字符时:
String str = a.next();
System.out.println(str);
//此时窗口输入字符,就会输出字符。*但是遇到空格,空格后的字符将不再输出。next()不能得到带有空格的字符串。要用nexLine.
String str1=a.nextline();
System.out.println(str1); //nextline()以enter作为结束符,可以获得空格。
//输出数据时(int,float等类型):
int i = a.next.Int;
float f=a.next.float;
}
}
if语句三种格式:
1. if(true){
执行代码块;
}
2. if(条件表达式){
执行代码块;
}
else{
执行代码块;
}
3. if(条件表达式){
执行代码块;
}
else if (条件表达式){
执行代码块;
}
……
else{
执行代码块;
}
1.switch(变量){
case 常量1:
语句1;
break;
case 常量2:
语句2;
break;
.....
default: //相当于else,注意冒号
语句;
break;
}
switch(表达式)中表达式的返回值必须是下述几种类型之一:byte,short,char,int,枚举,String;
case子句中的值必须是常量,且所有case子句中的值应是不同的;
default子句是可任选的,当没有匹配的case时,执行default
break语句用来在执行完一个case分支后使程序跳出switch语句块;如果没有break,程序会顺序执行到switch结尾
例子
//使用switch语句改写下列if语句:
int a = 3;
int x = 100;
if(a==1)
x+=5;
else if(a==2)
x+=10;
else if(a==3)
x+=16;
else
x+=34;
//解答:
int a=3;
int x=100;
switch(a){
case 1:
x+=5;
break;
case 2:
x+=10;
break;
case 3:
x+=16;
break;
default:
x+=34;
break;
}
区别:while 是先判断,后循环(使用较多),do-while是先循环后判断。
- 例:输出1-100之间所有整数和:
public class text1{
public stutic void main(String[] args){
int i=1;
int sum=0;
while(i<=100){
sum=i+sum;
i++;
}
System.out.println(sum);
}
}
//5050
for 循环(使用最多)
-for(初始化变量值;布尔表达式;迭代因子){ //迭代因子控制变量增减
循环体;
}
仔细理解图!false时直接退出循环。
-例1.求1-100之间的整数和:
.....
int sum=0;
for(int i=1;1<=100;i++){ //注意使用分号;
sum=sum+i;
}
System.out.println(sum);
//5050
-例 2.循环输出9-1之间的数
...
for(int i=9;i>0;i--){
System.out.print(i+"、");
}
-----------------------------------------------------------
例2 .打印1-100之间所有是7的倍数的整数的个数及总和 :
System.out.print("七的倍数有:");
int sum= 0;
int t=0;
for (int i=1;i<=100;i++){
if (i%7==0){
System.out.print(i+" ");
sum=i+sum;
t++;
}
}
System.out.println();
System.out.println("他们的和为:"+sum);
System.out.println("一共有:"+t+"个");
无限循环
public class Test12 {
public static void main(String[] args) {
for ( ; ; ) { // 无限循环: 相当于 while(true)
System.out.println("无限循环");
}
}
}
嵌套循环
-输出类似: 1 1 1
2 2 2
3 3 3
--代码--
for (int i=1; i <=3; i++) {
for(int j=1; j<=3; j++){ //当J=1,满足j<=3,输出i=1;循环j=2满足,继续输出 i=1,....,当j=4,不满足,退出;此时i进行第二次循 环,i=2,继续上一步操作,j从再一次循环。
System.out.print(i+" ");
}
System.out.println(); //每执行大循环时空格一次。
=============================================================================== ---打印9*9乘法表-
for (int i=1;i<=9;i++){
for (int j=1;j<=i;j++){ //如果j不小于i则会重复输出(如出现8*9=72,9*8=72)
System.out.print(i+"*"+j+"="+(i*j)+"\t");
}
System.out.println();
}
//声明:
数据类型+[]+变量 或者 数据类型+变量+[] 例如:int [] i;int i[];
//初始化:
--动态初始化:数组声明且为数组元素分配空间与赋值的操作分开进行
int [] i=new int [3]; //初始化一个长度为3的数组i;
i[0]=1; //手动赋值第一个数为1,第二个为4,第三个为5.
i[1]=4;
i[2]=5;
System.out.println(i[0]+i[1]+i[2]); //10
---静态初始化:在定义数组的同时就为数组元素分配空间并赋值。
int [] ii=new int []{2,3,4};
System.out.println(i[0]+i[1]+i[2]); //9
//数组中每个元素的位置从左到右是0,1,2,3.....上面i[0]=2,i[1]=3,i[2]=4.
– 定义并用运算符new为之分配空间后,才可以引用数组中的每个元素;
– 数组元素的引用方式:数组名[数组元素下标]
–数组元素下标从0开始;长度为n的数组合法下标取值范围: 0 —>n-1;如int a[]=new int[3]; 可引用的数组元素为a[0]、a[1]、a[2]
每个数组都有一个属性length指明它的长度,例如:a.length 指明数组a的长度(元素个数)
int [] i=new int []{3,4,5,6};
System.out.println(i.length); //4
int a[]= new int[5];
System.out.println(a[3]); //a[3]的默认值为0,此时没有赋值,要手动赋值即可
表示一维数组中的每一个元素内都包含一个数组
理解为矩阵,([x] [y]要),表示行,y表示列。注意他们的行和列的位置(下标)是从0开始的。
格式一(动态初始化):
int [][]arr=new int [3][4]
定义了名称为arr的二维数组, 二维数组中有3个一维数组,每一个一维数组中有2个元素。
给第一个一维数组1脚标位赋值为7写法是:arr[0] [1] = 7;
格式二(动态初始化)
int [][]arr=new int[3][]
二维数组中有3个一维数组。 每个一维数组都是默认初始化值null (注意:区别于格式1)
可以对这个三个一维数组分别进行初始化arr[0] = new int[3]; arr[1] = new int[1]; arr[2] = new int[2];
int [] []arr = new int [] [3] //非法
格式三:(静态初始化)
int [][] arr=new int [][]{
{3,2,4}, //第一个一维数组arr[0]={3,2,4};
{4,4,6}, //第二个一维数组arr[1]={4,4,6};
{6,6,8,8} //第三个一维数组arr[2]={6,6,8,8}
};
//第三个一维数组的长度表示方法为:arr[2].length;
如果我要输出元素3:System.out.println(arr[0][0]);
-----------------------------------------------------------------------------------------
**练习**
用for循环获取二维数组arr(上面的例子)中的所有元素的和。
int sum=0;
for (int i=0;i<arr.length;i++){ //此处不能i<=arr.length,因为i从0开始,并且arr 长度只有3,如果i=3,长度为4,无法满足。
for(int j=0;j<arr[i].length;j++){ //相当于要遍历所有元素一遍然后把他们加 起来。
sum=sum+arr[i][j];
}
}
System.out.println("和为:"+sum);
===================================分割线============================
*定义一个数组:
int [] arr=new int[]{9,3,6,1,8};
******求数组中的最大最小值******
for(int i=0;i<arr.length;i++){
if(max<arr[i]){ //求最小值反过来即可
max =arr[i]; //与其比较,然后将其最大值存放在max里。
}
}
System.out.println("max="+max); //max=9
********数组的复制(不是赋值)**********
int [] copy =new int [arr.length]; //copy要与原arr长度相同
for (int i=1;i<arr.length;i++){ //for循环起到遍历的作用
copy[j]=arr[i]; //arr每一个元素复制给copy
}
}
*************数组的反转***************
请使用冒泡排序算法编写程序实现数组{25,24,12,76,101,96,28}的排序;
int []arr =new int[] {25,24,12,76,101,96,28};
for (int i=0;i<arr.length;i++){
System.out.print(arr[i]+" "); //首先输出一遍原数组;
}
System.out.println(); //空格
for (int i=1;i<arr.length;i++){ //此循环代表总的需要对比的轮数,这里是6次,即要对比n-1次
for(int j=0 ; j<arr.length - i; j++){ //此循环代表每个元素需要对比的次数,从第一个数开始依次为6,5,4,3,2,1
if (arr[j]>arr[j+1]){ //即当i=1时,第一轮开始,25在这一轮要和六个数对比。
int temp=0; //输入一个数用来储存arr[j+1]的值
temp=arr[j+1];
arr[j+1]=arr[j]; //交换位置
arr[j]=temp;
}
}
}
for (int i=0;i<arr.length;i++){ //输出排序后的数组
System.out.print(arr[i]+" ");
}
==若想输出反序(大到小)if处改为if(arr[j]<arr[j+1])即可==
二者都是一种思想,面向对象是相对于面向过程而言的。面向过程,强调的是功能行为。面向对象,将功能封装进对象,强调具备了功能的对象。
面向对象更加强调运用人类在日常的思维逻辑中采用的思想方法与原则,如抽象、分类、继承、聚合、多态等。
**面像对象的三大特性:**封装(Encapsulation),继承(Inheritance),多态(Polymonrphism)。
面向对象的思想概述:
修饰符(public>protected>default>private)+ class +类名(自定义的名字,驼峰命名法){
//类,包括成员变量和方法;
}
public class Persion{
}
声明(定义)成员变量:
修饰符 + 数据类型 +变量名[=值] //说明:成员变量在类里面,不用赋值也可使用(int默认是0,String是null,long是0l等)
public class Persion{
public int age=18;
String name;
}
修饰符 +返回值类型 +方法名+(参数类型 +参数名1,参数类型 +参数名2… ){
//方法体
return 返回值; //当方法的返回值为void时return及其返回值可以省略;
}
public void showName(){
System.out.println(name);
}
//////////或者:
public int showAge(int a){
return a+1; //return后面的变量数据类型要i和前面的int一样;
}
///////一个完整的类:
public class Persion{
public int age=18;
String name;
public void showName(){ //方法创建
System.out.println(name);
}
}
//新建一个Test类,实例化上面那个Persion类
public class Test{
public static void main(String[] args){
Persion per=new Persion();
per.name="woshinidaye"; //给成员变量赋值;
per.showAge(); //调用方法;
System.out.println(name);
}
}//输出:18 woshinidaye
*************************分割线**************************************
//也可以直接在Persion类中这样写
public class Persion{
public int age=18;
public String name;
public void showName(){ //方法创建
System.out.println(name);
}
public static void main(String[] args){
Persion per=new Persion();
per.name="woshinidaye"; //给成员变量赋值;
per.showAge(); //调用方法;
System.out.println(name);
}
}
4.面向对象思想的”落地“法则
5.匿名对象
new Persion().showAge
new Persion().name="woshinidaye"
//像这种直接使用的就叫做匿名对象
概念:在同一个类中,允许存在一个以上的同名方法,只要它们的参数个数或者参数类型不同即可。
特点:与返回值类型无关,只看参数列表,且参数列表必须不同。(参数个数或参数类型)。调用时,根据方法参数列表不同来区别。
//返回两个整数的和
int add(int x,int y){return x+y;}
//返回三个整数的和
int add(int x,int y,int z){return x+y+z;}
//返回两个小数的和
double add(double x,double y){return x+y;}
///判断:
与void show(int a,char b,double c){}构成重载的有:
void show(int x,char y,double z){} //no
int show(int a,double c,char b){} //yes顺序不同也是重载
c) void show(int a,double c,char b){} //yes顺序不同也是重载
d) boolean show(int c,char b){} //yes
e) void show(double c){} //yes
f) double show(int x,char y,double z){} //no
g) void shows(){double c} //no
/**
* 用数组的方式来传递可变个数的参数
* 如果没有参数,就要定义一个空数组,或者null.
*/
public class Persion1 {
public void printInfor(String[] ass) {
for (int i = 0; i < ass.length; i++) {
System.out.println(ass[i]);
}
}
public static void main(String[] args){
Persion1 p1 = new Persion1();
String[] as = new String[]{"张三", "11"};
p1.printInfor(as); //形参里面要求是数组,所以要给他定义数组才能使用
String[] as1 = new String[]{"福建省xxx", "134324222"};
p1.printInfor(as1);
}
}
*************************分割线***********************************
/**、
*使用Java特有的...的方式来传递可变个数的参数,这种参数在使用时与数组的使用方式相同
* 如果没有参数就可以不填
* 这种...代表可以传递0到多个数组
* 如果一个方法有多个形参,可变的形参(...这种的参数)一定要放在所有的参数最后
* public void printInfor1(int d,String s,String...pp)这样是对的
* public void printInfor1(int d,String...pp,String s)这样就错了
* @param
*/
public class Persion1 {
public void printInfor1(String...ars) { //这里的String还可以是int,double,等等基本类型都 是可以使用的。ars只是一个参数名字可以任意换
for (int i = 0; i < ars.length; i++) {
System.out.print(ars[i]);
}
}
public static void main(String[] args) {
Persion1 p1 = new Persion1();
p1.printInfor1("张三 ","23岁 ","北京市xxxx"); //直接写想要的内容
}
}
使用new关键字创建的时候,除了分配内存空间之外,还会给创建好的对象进行默认初始化及对类中构造器的使用。
类中的构造器也称为构造方法,是在进行创建对象的时候必须要调用的。
特点:1.必须和类的名字相同;2.必须没有返回值,也没有void。3.默认构造器与类的修饰符一样。
public class Persion{
public Prsion(){ //一个类什么都不写,也会存在一个方法,系统会默认自动创建(不显示),这就是构造器。
}
}
********************初始化成员变量为默认值(无参)*************************
public class Persion{
String name;
public Prsion(){ //默认是不用写出来的,但是在传有参构造器时需要手动写。
}
public static void main(String[] args){
Persion per=new Persion;
System.out.println(per.name);
//输出:null
}
}
*****************给变量赋值(无参)**************
public class Persion{
String name;
public Prsion(){ //无参构造直接给变量赋值
name="woshinidaye";
}
public static void main(String[] args){
Persion per=new Persion;
System.out.println(per.name);
//输出:woshinidaye
}
}
public class Persion{
String name;
public Prsion(){ //如果要使用无参构造器的话此时必须写,不使用可不写
}
public Persion(String name){ //有参构造器
this.name=name; //左边的name是成员变量上定义的,右边这个代表是形参里面的
}
public static void main(String[] args){
Persion per=new Persion("woshinidaye"); //此时会根据方法重载来显示指定输出内容
// Persion per=new Persion();在有参构造器存在的情况下,要使用无参构造器前面就必须构造无参的
System.out.println(per.name);
//输出woshinidaye
}
}
/**
* 建立一个Pet类
*/
public class Pet {
String name;
int age;
public void shout(){
System.out.println("叫了一声");
}
}
***************************************************************
/**
* 建立Application来实例化兑对象
*/
public class Application {
public static void main(String[] args) {
Pet dog=new Pet();
dog.name="旺财";
dog.age=3;
dog.shout();
System.out.println(dog.age);
System.out.println(dog.name);
Pet cat=new Pet();
}
}
通常,应禁止直接访问一个对象中数据的实际表示,而通过操作接口来访问,称为信息隐藏。
要点:属性私有(private)只能在本类内部使用,get/set,大部分情况下是对属性(成员变量)使用。
通俗来说就是私有成员变量后,要在对象(类)中设立get/set公共(public)方法,可以在get/set方法中定义一些语句达到限制效果。然后在另一个类(有main方法的类)中直接可以调用get/set方法达到调用成员变量的效果。
public class Demo04 {
//类 private:私
//属性私有
private String name;
private int id;
private char sex;
public String getName() {
return name;
}
public void setName(String name) {
this.name = name; //左边的name是成员变量里面的,右边的name是形参里面的
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public char getSex() {
return sex;
}
public void setSex(char sex) {
this.sex = sex;
}
}
//实例化对象:
public class Application {
public static void main(String[] args) {
Demo04 d1=new Demo04();
d1.setId(12311);
System.out.println(d1.getId());
d1.setName("woshinidaye");
System.out.println(d1.getName());
d1.setSex('男');
System.out.println(d1.getSex());
}
}
***************************************
//举例:对年龄进行限制
public class Persion{
private int age;
public int getAge(){
return age;
}
public void setAge(int i){
if (i>100 ||i<0){
System.out.println("错误信息。请重新输入");
}else{
this.age=i;
}
}
}
//实例化对象:
public class Application {
public static void main(String[] args) {
Persion per1=new Persion();
per1.setAge(-1);
System.out.println(per1.getAge());
}
}
//输出错误信息。请重新输入
public class Person7 {
public Person7(){
//this("woshinidaye");//这样是错误的,因为persion7(String name)中已经有persion()了
}
public Person7(int age){
this.age = age;
}
public Person7(String name){
this();//等同于调用public Person7()
this.name = name;
}
public Person7(int age, String name){
this(1);//等同于调用public Person7(int age)
this.age = age;
this.name = name;
}
int age;
String name;
public void setName(String name){
this.name = name;
}
public void setName1(String name){
this.setName(name);
}
public void showInfo(){
System.out.println("姓名:" + this.name);
System.out.println("年龄:" + this.age);
}
}
多个类中存在相同属性和行为时,将这些内容抽取到单独一个类中,那么多个类无需再定义这些属性和行为,只要继承那个类即可。
此处的多个类称为子类,单独的这个类称为父类(基类或超类)。可以理解为:“子类 is a 父类”
语法:public class 子类(Subclass) extends父类(Superclass){}
在子类中,可以使用父类中定义的方法和属性,也可以创建新的数据和方法。
子类继承了父类,就继承了父类的方法和属性。
在Java 中,继承的关键字用的是“extends”,即子类不是父类的子集,而是对父类的“扩展”。
子类不能直接访问父类的私有变量,可以通过setter/getter获取。
每一个子类只能有一个唯一的父类,子类也可以可以有自己的子类,属于单继承。
java中默认每个类都直接或者间接继承object类。
Object类是所有Java类的根父类,如果在类的声明中未使用extends关键字指明其父类,则默认父类为Object类
//Persion 人:父类/基类
//Java中所有类都直接或者间接继承object类
//Java中只有单继承,没有多继承:一个儿子没有多个爹,一个爹可以有多个儿子。
public class Persion {
//private私有的。一般思想:属性(成员变量)私有化,方法共有化。
private int money=100;
int age;
String name;
public void say(){
System.out.println("别说话");
}
}
=====================================
//学生 is 人:子类/派生类;
//子类继承父类就会拥有父类全部方法,并且也可以拥有新方法
public class Student extends Persion{
public static void main(String[] args) {
Student stu = new Student();
stu.name="woshinidaye";
stu.age=18;
stu.say();
// stu.money();//无法调用,要使用get/set方法调用
}
}
定义:在子类中可以根据需要对从父类中继承来的方法进行改造,也称方法的重置、覆盖。在程序执行时,子类的方法将覆盖父类的方法。
要求:
继承时修饰符的使用区别:
super可用于访问父类中定义的属性
super可用于调用父类中定义的方法;
super可用于在子类构造器方法中调用父类的构造器
格式:super.成员变量;或者super.成员方法();
注意:
super调用父类构造器
**意义:**指定调用父类的哪个构造器,如果没有指定,则默认调用无参构造器。
格式:super([参数1,参数2…]);
//定义AAnimal父类
public class Animal{
public Animal(){ //定义无参构造器
System.out.println("叫了一声");
}
}
//定义Dog类继承Animal类
public class Dog extends Animal{
public static void main(String[] args){
Dog d=new Dog(); //在调用Dog类的默认无参构造方法,在这个过程中就使用父类的无参构造
}
}
//直接运行会发现输出了:叫了一声
//其实new Dog相当于在main方法前面前面运行了下面的代码,是默认执行的:
public Dog (){
super();
}
public class Animal{
public Animal(String name){ //定义了有参构造器。
System.out.println("我是一直"+name);
}
String name;
int age;
}
//定义Dog类继承Animal类
public class Dog extends Animal{
//在父类只有有参构可以使用的时候,子类必须显示的构建一个构造器来调用父类的有参构造器,并且调用父类的方法(super([参数1,参数2]))要写在第一行。
public Dog(){
//如果在这里加 int i=1; 报错,因为super(..);要写在首行。
super("沙皮狗"); //调用父类有参构造器;
}
public static void main(String[] args){
Dog d=new Dog();
}
}//输出:我是一只沙皮狗
3.如果子类构造器中既未显示用父类或本类的构造器,且父类中又没有无参构造器,则编译报错。
(理解:如果父类中只有有参构造,子类中无参构造器,报错,如果父类中有有参构造器和无参构造器,子类中无构造器,不报错,因为子类会默认访问父类的无参构造器,无论父类中是哪种情况,都可以通过super([参数…])来调用)。super关键字是用来指定调用无参还是有参构造器的,要使用无参就super();有参就super([参数])
public class Animal {
String name;
int age;
public Animal(int age,String name){ //有参构造
System.out.println(age);
System.out.println(name);
}
System.out.println("叫了一声");
}
}
//定义子类:
public class Dog extends Animal{
public void printIfo(){
super.name="wohi";
}
public static void main(String[] args) {
Dog d=new Dog();
}
}
//编译错误。虽然super()是默认执行无参构造,但在父类有参的情况下,在子类中要用无参(即子类中无构造器),父类就必须添加一个无参构造。
//如果想子类想使用父类的有参构造器,则可以通过显示的构造一个无参构造器,然后通过super([参数])来调用。
**********************看下面代码理解************************************
//父类
public class Animal {
String name;
int age;
public Animal(){ //建立空构造器
}
public Animal(int age,String name){ //建立有参构造器
System.out.println(age);
System.out.println(name);
}
public void shout(){
System.out.println("叫了一声");
}
}
//子类
public class Dog extends Animal{
public Dog(){ //用super()调用父类的空构造器
super();
}
public Dog(int a,String n){ //建立有参构造器,并用super(...)调用父类的有参构造器
super(a,n); //这里相当于把a和n当作参数传递到父类的构造器中
}
public void printIfo(){
super.name="wohi";
}
public static void main(String[] args) {
Dog d=new Dog(1,"金毛");//这里传递a=1,n="金毛"
//如果用 Dog d=new Dog();则用的就是 空构造器了
}
}//输出:1,金毛
/**
*创建Persion父类
*/
public class Persion {
public void print(){
System.out.println("i am a teacher");
}
public void showName(){
System.out.println("my name is 张三");
}
}
/**
*创建Student子类继承父类
*/
public class Student extends Persion{
int age;
@Override //重写父类的方法
public void print() {
System.out.println("i am a student");
}
public void eat(){
System.out.println("food");
}
}
/**
* 测试类
*/
public class Test {
public static void main(String[] args) {
//Studen能调用的方法都是继承父类的或者自己的
//Persion 父类,可以指向子类但不能使用子类独有的方法如这里子类的ea()方法
//如果要使用子类的独有方法,要通过强制类型转换; 如:((Student)s2).eat();
//对象能执行哪些方法,和左边有关,和右边关系不大
//当子类和父类中拥有同名方法(这里子类重写父类的方法print),程序会默认调用子类的方法。
Student s1=new Student();
Persion s2=new Student();
s1.print();//i am a student
//当子类和父类中拥有同名方法(这里子类重写父类的方法print),程序会默认调用子类的方法。
s2.print();//i am a student
// s2.eat();//报错,父类无法直接调用子类的方法(高转低类型),要通过强制类型转换((Student)s2).eat();
//对象能执行哪些方法,和左边有关,和右边关系不大
s2.showName();//my name is 张三
//studen能调用的方法都是继承父类的或者自己的(类型:低转高)
s1.showName();//my name is 张三
//s2.age=2;//报错,你的声明类型是父类的(高转低类型),现在不能使用子类的属性(成员变量),要强制类型转换((Student)s2).age=2;
}
}
instanceof操作符:
用于判断x是否是类A的对象,返回值为boolean类型。(这里的x表示声明后的对象名称,不能是类。)
要求x所属的类与类A必须存在父子类的关系,否则编译错误。
如果x属于类A的子类B,x instanceof A值也为true。
//Object>Persion>Teacher=Student
Object s1=new Persion();
Persion s2=new Student();
Persion s3=new Persion();
Student s4=new Student();
Teacher s5=new Teacher();
Object s0=new Object();
System.out.println(s1 instanceof Persion);//true
System.out.println(s2 instanceof Persion);//true
System.out.println(s3 instanceof Persion);//true
System.out.println(s4 instanceof Persion);//true
System.out.println(s4 instanceof Student);//ture
System.out.println(s1 instanceof Student);//false
System.out.println(s0 instanceof Persion);//false
//System.out.println(s5 instanceof Student);//编译报错,同级直接无法使用
Object类是所有Java类的根父类
如果在类的声明中未使用extends关键字指明其父类,则默认父类为Object类 。
//理解object作为根父类的作用:
//现在上题中已经建立persion父类,student子类,teacher子类
public class Test {
public void text01(Object obj){ //给test01方法传递一个不知道名字的类
public static void main(String[] args) {
Test t1=new Test();
Persion t2=new Persion();
Student t3=new Student();
t1.text01(t2);//相当于:Object t2=new Persion();
t1.text01(t3);
t1.text01(new Teacher());//与上面一样的操作
}
}
//理解object作为根父类的作用:
//现在上题中已经建立persion父类,student子类,teacher子类
public class Test {
public void text01(Object obj){ //给test01方法传递一个不知道名字的类
public static void main(String[] args) {
Test t1=new Test();
Persion t2=new Persion();
Student t3=new Student();
t1.text01(t2);//相当于:Object t2=new Persion();
t1.text01(t3);
t1.text01(new Teacher());//与上面一样的操作
System.out.println(t2.equals(t3));//false
Persion t4=new Persion();
System.out.println(t4.equals(t2));//false,在堆内存里面重新开一个new persion他们指向的地址都不一样。
t4=t2;
System.out.println(t4.equals(t2));//true,此时已经直接赋值为相同地址的对象了
}
}
System.out.println(t2.hashCode());
System.out.println(t2.toString());
System.out.println(t2);//或者直接这样也是默认调用toString方法的,效果一样。
public class test {
public static void main(String[] args) {
Integer i=1;//自动装箱
int i1=i;//自动拆箱
System.out.println(i1);
Boolean b=false;//自动装箱
boolean b1=b;//自动拆箱
System.out.println(b1);
//字符串类型转化为基本数据类型;
string str1="122";
String str2 = "30.3" ;
String str3 = "false" ;
int p=Integer.parseInt(str1);
float f=Float.parseFloat(str2);
boolean bo=Boolean.parseBoolean(str3);
//基本数据类型转化为字符串类型;
int i=222;
float f=3.3;
boolean bo=false;
String str1=String.valueOf(i);
String str2=String.valueOf(f);
String str3=String.valueOf(bo);
}
}
用static修饰的属性(成员变量)和方法分别叫做类变量也叫静态变量和类方法;
在Java类中,可用static修饰属性、方法、代码块、内部类;
被修饰后的成员具备以下特点:
随着类的加载而加载
优先于对象存在
修饰的成员,被所有对象所共享
访问权限允许时,可不创建对象,直接被类调用(类名.方法 或者 类名.属性)。
public class test {
public static int age;
public String name ;
public static void showName (){
System.out.println("my name is 张三");
}
public static void main(String[] args) {
test.showName();//直接可以调用方法和属性
test.age=21;
System.out.println(age);
}
}
所谓类的单例设计模式,就是采取一定的方法保证在整个的软件系统中,对某个类只能存在一个对象实例,并且该类只提供一个取得其对象实例的方法。
如果我们要让类在一个虚拟机中只能产生一个对象,我们首先必须将类的构造方法的访问权限设置为private,这样,就不能用new操作符在类的外部产生类的对象了,但在类内部仍可以产生该类的对象。因为在类的外部开始还无法得到类的对象,只能调用该类的某个静态方法以返回类内部创建的对象,静态方法只能访问类中的静态成员变量,所以,指向类内部产生的该类对象的变量也必须定义成静态的。
/**
*饿汉式
*/
public class Singlen {
private Singlen(){ // //1.将构造器私有化,保证在此类的外部,不能调用本类的构造器。
}
private static Singlen onlyone=new Singlen(); //私有的,只能在类的内部访问
public static Singlen getSinglen(){ //getSingle()为static,不用创建对象即可访问
return onlyone;
}
}
//测试类:
public class Application {
public static void main(String[] args) {
Singlen s1=Singlen.getSinglen();//访问静态方法;达到new Singlen()的效果
Singlen s2=Singlen.getSinglen();//s1,s2都使用的是同一个new Singlen()
//测试:
if (s1==s2){
System.out.println("s1 is equals to s2!");
}
}
}
/**
* 懒汉式
*/
public class Singlen {
private Singlen() { //1.将构造器私有化,保证在此类的外部,不能调用本类的构造器。
}
private static Singlen instance = null; //2.先声明类的引用, //4.也需要配合static的方法,用static修饰此类的引用。
public static Singlen getSinglen() { //3.设置公共的方法来访问类的实例
//3.1如果类的实例未创建,那些先要创建,然后返回给调用者:本类。因此,需要static 修饰。
if(instance == null){
instance = new Singlen();
}
//3.2 若有了类的实例,直接返回给调用者。
return instance;
}
}
//测试类:
public class Application {
public static void main(String[] args) {
Singlen s1=Singlen.getSinglen();//访问静态方法;达到new Singlen()的效果
Singlen s2=Singlen.getSinglen();//s1,s2都使用的是同一个new Singlen()
//测试:
if (s1==s2){
System.out.println("s1 is equals to s2!");
}
}
}
public static void main(Sring[] args){
}
public class demo01 {
String name;
public demo01(){
this.name="张三";
System.out.println("这是一个构造器");
}
//这就是非静态代码块
{
System.out.println("这是一个代码块1");
}
{
System.out.println("这是一个代码块2");
}
{
System.out.println("这是一个代码块3");
}
}
//测试类:
public class Test {
public static void main(String[] args) {
demo01 d=new demo01();
}
}
/*
执行顺序:
第一步:类属性的默认初始化和显示初始化;
第二步:执行代码块的内容(有多少个代码块就执行多少次);
第三步:执行构造器;
输出:
这是一个代码块1
这是一个代码块2
这是一个代码块3
这是一个构造器
*/
public class demo01 {
String name;
static int age;
public demo01(){
this.name="张三";
System.out.println("这是一个构造器");
}
//静态代码块:
static {
age=1;
showAge(); //这里只能执行静态方法和属性
System.out.println("====这是一个静态代码块====");
}
public static void showAge(){
System.out.println(age);
}
}
//测试类:
public class Test {
public static void main(String[] args) {
demo01 d=new demo01();
demo01 d1=new demo01();
}
}
/*
输出:
1
====这是一个静态代码块====
这是一个构造器
*/
public class demo01 {
String name;
static int age;
public demo01(){
this.name="张三";
System.out.println("这是一个构造器");
}
//这就是非静态代码块
{
System.out.println("这是一个非静态代码块1");
}
{
System.out.println("这是一个非静态代码块2");
}
{
System.out.println("这是一个非静态代码块3");
}
//静态代码块:
static {
age=1;
showAge(); //这里只能执行静态方法和属性
System.out.println("====这是一个静态代码块====");
}
public static void showAge(){
System.out.println(age);
}
}
//测试类:
public class Test {
public static void main(String[] args) {
demo01 d=new demo01();
demo01 d1=new demo01();
}
}
//在运行中,非静态代码块每次new对象都要重新执行,静态代码块只执行一次
/*
输出:
1
====这是一个静态代码块====
这是一个非静态代码块1
这是一个非静态代码块2
这是一个非静态代码块3
这是一个构造器
这是一个非静态代码块1
这是一个非静态代码块2
这是一个非静态代码块3
这是一个构造器
*/
非静态代码块:没有static修饰的代码块
1.可以有输出语句。
2.可以对类的属性声明进行初始化操作。
3.可以调用静态和非静态的变量或方法。
4.若有多个非静态的代码块,那么按照从上到下的顺序依
次执行。
5.每次创建对象的时候,都会执行一次。且先于构造器执行
静态代码块:用static修饰的代码块
1.可以有输出语句。
2.可以对类的属性声明进行初始化操作。
3.不可以对非静态的属性初始化。即:不可以调用非静态的属
性和方法。
4.若有多个静态的代码块,那么按照从上到下的顺序依次执行。
5.静态代码块的执行要先于非静态代码块。
6.静态代码块只执行一次。
public class demo01 {
String name;
static int age;
public demo01(){
this.name="张三";
}
public void showInf(){
System.out.println("你好!");
}
}
//测试类(注意理解匿名内部类的创建和使用):
public class Test {
public static void main(String[] args) {
demo01 de=new demo01(){ //构建了一个匿名内部类!相当于在此构建了一个没有类名的demo01的子类;
//没有类名就说明不能用显示的new的方式来创建对象,即没有构造器,无法初始化属性,此时就需要代码块{}来做初始化的工作、
//问题,现在想把demo01中的name改为李四,但是不想动dem01中的代码,就要使用代码块代替构造方法。
{
super.name="李四";
}
@Override //因为是demo01的子类,所以可以重写父类的方法
public void showInf() {
System.out.println("我是没名字的子类");
}
};
System.out.println(de.name);
de.showInf();
}
}
/*
输出:
李四
我是没名字的子类
*/
public class demo {
//final标记的变量变为常量,常量必须赋值。、
//final+static修饰的变量为全局常量;
//final修饰的类不能被继承;
//final修饰的方法不能被重写;
final String NAME="";//常量,名称大写,且只能被赋值一次,且必须赋值才行;
}
/**
* 抽象abstract:理解:一个动物的类为父类,狗类和鱼类为子类,父类里面有一个移动方法,但狗和鱼的移动方法不一样
* 所以我只能让父类抽象化,子类具体化;
* 用abstract关键字来修饰一个类时,这个类叫做抽象类;
* 用abstract来修饰一个方法时,该方法叫做抽象方法。该方法只有声明,没有方法体,以分号结束。
* 如:public abstract void move([参数类型 参数名...]);
* 抽象类也可以继承抽象类(子类也可以作为抽象类)
* 1.含有抽象方法的类必须被声明为抽象类。
* 2.抽象类不能被实例化。抽象类是用来作为父类被继承的,抽象类的子类必须重写父类的抽象方法,并提供方法体。若没有重写全部的抽象方法,仍为抽象类。
* 3.不能用abstract修饰属性、私有方法、构造器、静态方法、final的方法。
*/
public abstract class Animal {
public abstract void move(); //只要有一个抽象方法,类就必须是抽象类
}
class Fish extends Animal{//直接建立一个子类继承父类
@Override //重写animal的方法此类才能用//理解如果没有重写抽象类的所有方法,则这个类依然是抽象类
public void move() {
System.out.println("游");
}
}
class Dog extends Animal{
@Override
public void move() {
System.out.println("跑");
}
}
abstract class Bird extends Animal{ //抽象类也可以继承抽象类
@Override
public void move() {
}
public abstract void shut();//只要有一个抽象方法,类就必须是抽象类
}
class littleBirld extends Bird{
@Override
public void shut() {
System.out.println("zzz");
}
}
//测试类:
public class test {
public static void main(String[] args) {
Fish fish=new Fish();
fish.move();
Dog dog=new Dog();
dog.move();
}
}
/*
*游
*跑
/*
接口(interface)是抽象方法和常量值的定义的集合。
从本质上讲,接口是一种特殊的抽象类,这种抽象类中只包含常量和方法的定义,而没有变量和方法的实现。
特点:
写法:
//接口test
public interface Test {
int ID=1; //public static final int ID=1;
void method();//public abstract void test01();
}
//接口test01
public interface Test01 {
void method01();//public abstract void method();
}
//接口可以继承接口
public interface Test02 extends Test01 {
}
实现接口
public class Application implements Test,Test01{
@Override
public void method() { //需要实现接口的所有方法,不然不能使用,要被定义为抽象类;
}
@Override
public void method01() {
}
}
public class Test04 extends Application implements Test01 ,Test{
//一个类可以实现多个接口,接口也可以继承其它接口。
}
接口的主要用途就是被实现类实现。(面向接口编程)
与继承关系类似,接口与实现类之间存在多态性:
//接口test
public interface Test {
int ID=1; //public static final int ID=1;
void method();//public abstract void test01();
}
//实现类
public class Application implements Test,Test01{
@Override
public void method() {
}
public static void main(String[] args) { //main方法主程序入口
Test t=new Application();//体现多态性
t.method(); //可以发现他只能调用自己的方法
}
}
接口对比抽象类:
public class Test3 { //外部类
int i;
public int z;
private int k;
class A{ //内部类A
int i;
public void setTest3Fileds(){
Test3.this.i = 1; //调用外部类的属性
Test3.this.z = 2;
Test3.this.k = 3;
}
public void set(){
this.i = 10; //调用自己的属性
}
}
//如果内部类是static的,就不能使用外部类的非static的成员
static class B{
}
abstract class C{
}
class D extends C{ //内部类可以继承
}
public void setInfo(){
new A().setTest3Fileds();//外部的类要用自己的内部类的方法,得先new内部类的对象
}
public void showInfo(){
System.out.println(this.i);
System.out.println(this.z);
System.out.println(this.k);
}
public static void main(String[] args) {
Test3 t = new Test3();
t.setInfo();
t.showInfo();
}
}//输出1,2,3
public class Test{
public static void main(String[] args) { //调用showIfo方法来实现调用B,C的方法;先看下面再回来看这一步
A a=new A();
a.showInfo();
}
}
class B{ //一个类B
public void testB(){
}
}
class C{ //一个类C
public void testC(){
}
}
/**
* 现在我想让A同时继承B和C,该怎么办?Java中是不能一个子类类继承多个父类的;
* 用内部类变相实现多重继承;
* 解决java不能多重继承的问题
*/
class A { //类A
public class InnerB extends B{ //在类A中创建一个内部类继承B;
@Override
public void testB() { //可以重写B的方法
System.out.println("这是重写之后的B的方法");
}
}
public class InnerC extends C{//在类A中创建一个内部类继承C;
@Override
public void testC() { //可以重写C的方法
System.out.println("这是重写之后的C的方法");
}
}
public void showInfo(){ //在A中建立方法来调用testB和testC方法
new InnerB().testB(); //先new才能调用testB方法,这里使用了匿名类,直接new InnerB来使用,不给他名称
new InnerC().testC();
}
}
//输出:这是重写之后的B的方法,这是重写之后的C的方法
异常:在Java语言中,将程序执行中发生的不正常情况称为“异常”。
分类:
Error: JVM系统内部错误、资源耗尽等严重情况
Exception: 其它因编程错误或偶然的外在因素导致的一般性问题,例如:•空指针访问•试图读取不存在的文件•网络连接中断。
常见异常:
•错误的类型转换
•数组下标越界
•空指针访问
•从一个不存在的文件中读取数据
•越过文件结尾继续读取EOFException
•连接一个不存在的URL
3.异常处理机制:(只能处理Exception异常)
未处理异常时,当程序运出错时,后面的代码就会全部终止运行,所以我们要制作异常机制来将可能会发生异常的代码集中起来,与正常代码分开
抓抛模型:
try{
//可能发生异常的语句,在这里如果有异常代码,则在try{}中后面的代码将不再被执行
}catch(Exception类或者其子类e){ //()里面如果未知异常的类型,就用所有异常的父类Exception
//对捕获的异常进行处理
}finally{ //finally关键字表示最终执行的代码,可以不用写
}
//详情:
=====================================================
/**
* 异常语句,程序将不执行剩下的代码。
*/
public class Test {
public static void main(String[] args) {
int i = 0;
System.out.println(3/i);
System.out.println("ok");
}
} //Exception in thread "main" java.lang.ArithmeticException: / by zero
/**
* 捕获异常语句,程序可以正常运行
*/
public class Test {
public static void main(String[] args) {
int i = 0;
try{
System.out.println(3/i);
}catch (Exception e){
//e. e.printStackTrace();表示获取这个异常到底是是什么类型,可以不写
//System.out.println(e.getMessage());//getMessage表示异常信息,可以不写
}
System.out.println("ok");
}
}//ok
/**
*finally的意义:表示最终执行的代码;
*/
.....
int i = 0;
try{
System.out.println("1");
System.out.println(3/i); //此处异常后,下面的输出2就不执行了
System.out.println("2");
}catch (Exception e){
System.out.println("3");
}finally{
System.out.println("4");
}
//输出1,3,4,
•如果一个方法(中的语句执行时)可能生成某种异常,但是并不能确定如何处理这种异常,则此方法应显式地声明抛出异常,表明该方法将不对这些异常进行处理,而由该方法的调用者负责处理。
•在方法声明中用 throws 子句可以声明抛出异常的列表,throws后面的异常类型可以是方法中产生的异常类型,也可以是它的父类。
[修饰符] 返回值类型(或者void) 方法名 ([参数类型+ 参数名称]...)throws 异常1,异常2(直接用Excpetion所有异常的父类),....{
//方法体
}
//举例
public void readFile(String file) throws FileNotFoundException{
....
// 读文件的操作可能产生FileNotFoundException类型的异常
FileInputStream fis = new FileInputStream(file);
....
}
public class Test01 {
public void test(int i)throws Exception{ //定义一个抛出异常的有参方法
int b=1/i;
System.out.println(b);
}
public static void main(String[] args) { //也可以在main方法出继续抛出异常,但是直接抛到jvm内无法处理
Test01 t=new Test01();
try{ //用throws抛出异常以后在调用方法时必须用try..catch来捕捉;
t.test(0);
}catch(Exception e){
System.out.println(e.getMessage());
}
}
}//输出:/ by zero
//父类A
public class A{
public void method() throws IOException{
...
}
}
//子类B1
public class B1 extends A{
public void method() throws FileNotFoundException{
....
}
}
public class B2 extends A{
public void method() throws Exception{ //Erro 错误
}
}
人工抛出异常(throw关键字):人工抛出自己命名的异常名称
与throws不同,throw用在方法体内,并且抛出的是一个异常对象,并且也要通过throws关键字和try…catch来捕获处理。
//语法格式
[修饰符] 返回值类型(或者void) 方法名 ([参数类型+ 参数名称]...)throws 异常1,异常2(直接用Excpetion所有异常的父类),....{
//方法体...
throw new Exception类或者子类构造方法;
}
//举例
public class Test02 {
public void printAge(int age) throws Exception {
if (age < 0) {
//进行逻辑判断,如果年龄为负数,则抛出异常
throw new Exception("输入年龄有误,必须是正整数");
} else {
System.out.println("年龄为:" + age);
}
}
public static void main(String[] args) {
Test02 t2 = new Test02();
int age=-1;
//定义try ..catch来捕获异常
try {
t2.printAge(age);
}catch (Exception e){
System.out.println("捕获的异常信息为:"+e.getMessage());
}
}
}
/**
* 输出:捕获的异常信息为:输入年龄有误,必须是正整数
*/
4.自定义异常类:(了解)
public class MyException extends Exception{//自定义异常类
public MyException(String msg){ //构造器
super(msg); //调用父类的构造方法
}
}
//定义一个类
public class Test02 {
public void printAge(int age) throws MyException { //抛出自定义异常类:
if (age < 0) {
//进行逻辑判断,如果年龄为负数,则抛出异常
throw new MyException("输入年龄有误,必须是正整数");
} else {
System.out.println("年龄为:" + age);
}
}
......
//进行一系列try... catch捕获。
•①、集合只能存放对象。比如你存一个 int 型数据 1放入集合中,其实它是自动转换成 Integer 类后存入的,Java中每一种基本类型都有对应的引用类型。
•②、集合存放的是多个对象的引用,对象本身还是放在堆内存中。
•③、集合可以存放不同类型,不限数量的数据类型。
集合 | Collection(单列集合的根接口) | Map(双列集合根接口) |
---|---|---|
Set(接口):无序、不可重复的集合——由HashSet和TreeSet类来实现 | Map(接口):具有映射关系的集合——HashMap类实现 | |
List(接口):有序,可重复的集合——由ArrayList类来实现 |
HashSet 是 Set 接口的典型实现,大多数时候使用 Set 集合时都使用这个实现类。我们大多数时候说的set集合指的都是HashSet.
//导入相应包
import java.util.HashSet;
import java.util.Iterator;
import java.util.Set;
public class Test {
public static void main(String[] args) {
Set set= new HashSet();
set.add(1); //添加元素
set.add("a");
System.out.println(set); //输出:[1, a]
set.remove("a"); //移除元素
System.out.println(set); //输出[1]
set.contains("a"); //判断是否存在该元素
System.out.println(set.contains("a")); //false
set.clear();//清空集合
System.out.println(set);//[]
}
}
public class Test {
public static void main(String[] args) {
Set set= new HashSet();
/**
* 集合遍历讲解
*/
set.add("a");
set.add("b");
set.add("c");
set.add("d");
set.add("e");
System.out.println(set);
//1.使用迭代器遍历集合(推荐使用)
Iterator it =set.iterator();
while (it.hasNext()){
System.out.println(it.next());
}
//2.使用for each迭代集合(多数使用)
for(Object obj :set){ //意思是把set的每一个值取出来赋值给obj,直到循环所有的set元素
System.out.println(obj);
}
//获取集合元素个数
System.out.println(set.size());//5
//如果在集合中再添加一个相同元素:set集合存的值是不重复的。
set.add("a");
System.out.println(set);//输出后发现a并没有被添加,输出set.size()大小也没变
//添加null元素
set.add(null);
System.out.println(set);//输出:[null, a, b, c, d, e],null在第一个,说明无序性
}
}
/**
* 泛型的使用,接着上面的代码
*/
....
set.add(1); //可知set集合可以存放各种类型的对象
set.add(true);
set.add("sdd");
//现在想让集合中只能存入同样类型的对象,怎么办?
Set<String> set1= new HashSet<String>(); //这样就只能存放字符串类型的对象
set1.add("asx");
//set1.add(1)报错
Set<Integer> set2= new HashSet<Integer>(); //这样就只能存整数类型的对象
set2.add(2);
//Object是所有类的父类,所以Set
//也就是Set set= new HashSet();默认是Object类型,所以可以存放任何类型
}
}
import java.util.Iterator;
import java.util.Set;
import java.util.TreeSet;
public class Test1 {
public static void main(String[] args) {
Set<Integer> set = new TreeSet<Integer>();
//自然排序:TreeSet会按大小默认排序(在同一泛型内才行)
set.add(1);
set.add(5);
set.add(3);
set.add(8);
System.out.println(set);//输出:[1, 3, 5, 8]
//其它使用方法与HashSet相同
set.contains(9);//false,集合内不存在9
//set.clear();//清除集合
//1迭代器遍历:
Iterator it = set.iterator();
while(it.hasNext()){
System.out.println(it.next());
}
// 2.使用for each迭代遍历集合
for (Integer i:set ){
System.out.println(i);
}
}
}
/**
* TreeSet的定制排序:实现comparator接口
* 把student对象放到TreeSet集合中并且按照年龄排序
*/
class Student implements Comparator <Student>{ //要添加泛型,Student类型
int age;
String name;
public Student(){ //Student空构造器
}
public Student (int age,String name){ //有参构造器
this.age=age;
this.name=name;
}
@Override //重写compare方法。自己定制比较规则
public int compare(Student student, Student t1) {
if(student.age>t1.age){ //前一个大于后一个,这一套表示正序排列,
return 1; //如果要倒序,直接调换<>位置即可
}else if(student.age<t1.age){
return -1;
}else {
return 0;
}
}
}
public class Test5 {
public static void main(String[] args) {
Student stu1=new Student(14,"张三");
Student stu2=new Student(16,"李四");
Student stu3=new Student(9,"王五");
Student stu4=new Student(19,"赵六");
TreeSet<Student> set=new TreeSet<Student>(new Student());//new Student()的原因是为了传入comparator
set.add(stu1);
set.add(stu2);
set.add(stu3);
set.add(stu4);
for(Student s:set){
System.out.println(s.age+" "+s.name);
}
}
}
import java.util.ArrayList;
import java.util.List;
import java.util.Set;
public class Test2 {
public static void main(String[] args) {
List<String> list=new ArrayList<String>();
list.add("b"); //第一个,索引下标0;
list.add("d");//第二个,索引下标1;
list.add("c");//第3个,索引下标2;
list.add("a");//第4个,索引下标3;
System.out.println(list);//输出:[b, d, c, a]
System.out.println(list.get(2));//输出:c,可以通过索引来访问指定位置的集合元素
list.add("d");
System.out.println(list);//[b, d, c, a, d],可重复;
list.add(1,"f");//在指定索引下标的位置插入数据
System.out.println(list);//[b, f, d, c, a, d]
//新建一个集合l并添加元素
List<String > l=new ArrayList<>();
l.add("1123");
l.add("3322");
//在指定的索引下标内插入集合
list.addAll(2,l);//在2位置插入l集合
System.out.println(list);//输出:[b, f, 1123, 3322, d, c, a, d]
System.out.println(list.indexOf("d"));//4,获取指定元素在集合中第一次出现的索引下标
System.out.println(list.lastIndexOf("d"));//7,获取指定元素在集合中最后一次出现的索引下标
list.remove(2);//()里面写指定的索引下标位置,根据下标移除元素
System.out.println(list);//[b, f, 3322, d, c, a, d],
list.set(1,"fff");//根据指定的索引下标修改元素
System.out.println(list);//[b, fff, 3322, d, c, a, d]
List <String> sub=list.subList(2,4);//根据索引下标的起始位置截取一段新的集合,元素的索引截取时,不包括最后一个元素也就是[2,4)
System.out.println(sub);//[3322, d],包括第一个,不包括后面一个,理解为:[2,4)
System.out.println(list.size());// 7,集合长度
}
}
Map集合
Map 用于保存具有映射关系的数据,因此 Map 集合里保存着两组值,一组值用于保存 Map 里的 Key,另外一组用于保存 Map 里的 Value。
Map 中的 key 和 value 都可以是任何引用类型的数据
Map 中的 Key 不允许重复,即同一个 Map 对象的任何两个 Key 通过 equals 方法比较中返回 false。
Key 和 Value 之间存在单向一对一关系,即通过指定的 Key 总能找到唯一的,确定的 Value。
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
public class Test3 {
public static void main(String[] args) {
// Map
Map<String ,Integer> map=new HashMap<String, Integer>();
map.put("b",1);//添加数据,key不能相同,且唯一,value可以是相同的
map.put("c",2);
map.put("e",2);
System.out.println(map);//{b=1, c=2, e=2}
System.out.println(map.get("b"));//输出:1,根据key取value的值
map.remove("c");//根据key移除键值对
System.out.println(map);//{b=1, e=2}
System.out.println(map.size());//map集合的长度
System.out.println(map.containsValue(1));//true,,判断当前的集合是否包含指定的value。
System.out.println(map.containsKey("a"));//false,判断当前的集合是否包含指定的key。
System.out.println(map.keySet());//[b, e],获取map集合的key集合
System.out.println(map.values());//[1, 2],获取所有value值
//遍历map集合方法一,通过map.KeySet();
Set<String> keys=map.keySet();//获取map所有key的集合(如上,是一个set集合)
//for each遍历
for (String key :keys){
System.out.println("key:"+key+",value:"+map.get(key));//输出:key:b,value:1 key:e,value:2
//map.get(key)表示根据key值取value的值。
}
//遍历map集合的方法二:使用迭代器Iterator遍历
Set keySet=map.keySet(); //获取键的集合
Iterator it=keySet.iterator();
while (it.hasNext()){
Object key=it.next();
Object vaule =map.get(key);
System.out.println(key+":"+ vaule);
}
}
}
public class Test3 {
public static void main(String[] args) {
Map<Integer,String> map= new TreeMap<Integer, String>();
map.put(2,"a");
map.put(1,"d");
map.put(5,"h");
map.put(3,"r");
System.out.println(map);
}
}/*输出:
{1=d, 2=a, 3=r, 5=h}
*/
//再测试:
Map<String ,String> map1= new TreeMap<String, String>();
map1.put("ae","a");
map1.put("d","t");
map1.put("a","y");
map1.put("g","i");
map1.put("1","ac");
map1.put("8","a");
System.out.println(map1);
/*输出:
{1=ac, 8=a, a=y, ae=a, d=t, g=i}
体会排序功能
*/
import java.util.*;
public class Test4 {
public static void main(String[] args) {
List<Integer> list=new ArrayList<Integer>();
list.add(1);
list.add(4);
list.add(6);
list.add(2);
System.out.println(list);//[1, 4, 6, 2]
//反转集合
Collections.reverse(list);
System.out.println(list);//[2, 6, 4, 1]
//随机排序
Collections.shuffle(list);
System.out.println(list);//[2, 4, 1, 6]
//list集合字典升序排列
Collections.sort(list);
System.out.println(list);//[1, 2, 4, 6]
//指定交换集合中的几个元素
Collections.swap(list,0,3);//调换第0 个元素和第三个元素
System.out.println(list);
//查找最大值,最新小值
System.out.println(Collections.max(list));//6
System.out.println(Collections.min(list));//1
//返回指定元素出现的次数
list.add(1);//在集合中再添加一个1;
System.out.println(Collections.frequency(list, 1)); //2
System.out.println(Collections.frequency(list, 2)); //2
System.out.println(Collections.frequency(list, 3)); //0,集合中没有3
// 用新的值替换所有旧值
Collections.replaceAll(list,1,111);//把1换成111
System.out.println(list);//[6, 2, 4, 111, 111]
}
}
定义:限定集合只能存入指定类型的元素;
public class Test5{
public static void main(String[] args){
List list=new ArrayList//不使用泛型时,默认是Object类,即集合中可以存入任何类型的元素;
List <String> lit = new ArrayList<String>(); //集合中只能存入String类型
List <Ingeter> li = new ArrayList<Ingeter>();//集合中只能存入整型元素;
li.add("name");//报错,是在编译期间发现的问题
}
}
注意:泛型只在编译期有效,不会进入运行时段。
public class Test06 {
/**
*泛型类,名字T可以使用任意A,B,V...
*一般用T,意为type,先在类名后面加,,表示他是一个泛指类,然后在new的时候具体使用
*意义:在面向对象时(new 对象时),达到将对象类型限定(指定对象类型)的效果。
*/
}
class A<T>{
private T key;
public void setKey(T key){
this.key=key;
}
public T getKey(){
return this.key;
}
public static void main(String[] args) {
A<String> a1=new A <String>();//在new A对象时指定数据类型是String
a1.setKey("xxxx");//对象使用setKey(T key)方法,中的key形参就是String类型
String s=a1.getKey();//T getKey()返回的对象也是String类型;
A<Integer> a2= new A<Integer>();//限定对象类型为整数型
a2.setKey(1);
// a1=a2;报错,类型不同不能赋值
}
}
====================================分割线==========================================
/**
* 泛型接口
*/
public class Test07 { //测试类,先看下面下面接口的代码
public static void main(String[] args) {
B1<Object> b1=new B1<Object>();//B1没有限定类型,就可以使用任意类型
B1<String> b2=new B1<String>();
//不同的是,new确定好泛型类型的对象时,就不能再指定泛型了,因为前面已经指定过
B2 b3=new B2();
// B2 b3=new B2();这样反而报错。
}
}
interface IB<T>{ //接口IB
T test(T t); //接口方法
}
class B1 <T> implements IB<T>{ //未传入泛型实参时,与泛型类的定义相同,在声明类的时候,需将泛型的声明也一起加到类中
//如果不声明泛型,如:class B1 implements IB,编译器会报错:"Unknown class"
@Override
public T test(T t) {
return t;
}
}
class B2 implements IB<String>{ //实现接口时指定接口的泛型具体数据类型,实现类B2后面就可以不用写类型,
@Override
public String test(String s) {//并且实现类中所有方法的类型和返回类型都变为和泛型一样
return null;
}
}
===================================分割线===========================================
/**
* 泛型方法
*/
class C<T>{ //定义一个泛型类;
/**
* 无返回值的泛型方法
*/
public <T>void test(T s){
T t=s;
}
/**
* 有返回值的泛型方法
*/ public <T> T test1(T s){
return s;
}
/**
* 型参为可变参数的泛型方法
*/
public <T> void test2 (T...args){
for(T tt:args){
System.out.println(tt);
}
}
public static void main(String[] args) {
C <Object>c=new <Object> C();
//泛型方法:在调用之前没有固定的数据类型
//调用时传入的参数是什么类型就会把泛型改为什么类型,自动的
c.test(11);//传递的参数是integer,泛型就变成integer,返回值也是变为Integer
c.test("wowo" );//传递的参数是String,泛型就变成String,返回值也是变为String
c.test(true);//...
}
}
/**
* 通配符?的使用
*1:不确定集合中的元素具体的数据类型
*/
public class Tast09 {
public static void main(String[] args) {
A1 a=new A1();
List<String > l1=new ArrayList<String>();
a.test(l1);
List<Integer> l2=new ArrayList<Integer>();
a.test(l2);
}
}
class A1{
public void test(List<?> list){//test方法需要一个list集合的参数,不确定list存储的类型是什么
}
}
/**
* 通配符?的使用
*2:有限制的通配符
*/
public class Tast09 {
public static void main(String[] args) {
A1 a=new A1();
List<C1> Lc=new ArrayList<C1>();
a.test(Lc);
List< D1> Ld=new ArrayList<D1>();
a.test(Ld);
List< B3> Le=new ArrayList<B3>();
//a.test(Le);报错,B3是C1的父类,不在范围内,所以不能添加le集合作为test的形参
}
}
class A1{
public void test(List<? extends C1> list){//list的参数元素数据类型的范围是在C1及其子类中
}
}
class A2{}
class B3 extends A1{}
class C1 extends B3{}
class D1 extends C1{}
/**
* public void test(List super C1> list)时表示只允许泛型为C1及其C1的父类才能使用,和上面演示一样
* extends Comparable>只允许泛型为实 现Comparable接口的实现类的引用调用
*/