一:java基础
java类和变量,常量
配置环境变量(我们当时是填空,具体的问题记不清了,填的JAVA_HOME,CLASSPATH,这个题卡住了很多人,没有人想到会考这个)
(1)变量的概念和分类:
概念:内存中的存储空间,用于存放运算过程中的数据
变量分类:局部变量、成员变量、类变量
局部变量:
局部变量声明在方法、构造方法或者语句块中;
局部变量没有默认值,所以局部变量被声明后,必须经过初始化,才可以使用。
不允许被修饰符修饰,局部方法也一样
未经赋值也不可以被使用
成员变量:
实例变量声明在一个类中,但在方法、构造方法和语句块之外;
实例变量具有默认值。数值型变量的默认值是0,布尔型变量的默认值是false,引用类型变量的默认值是null。变量的值可以在声明时指定,也可以在构造方法中指定;
类变量(也叫静态变量):
Java 中被 static 修饰的成员称为静态成员或类成员。它属于整个类所有,而不是某个对象所有,即被类的所有对象所共享。静态成员可以使用类名直接访问,也可以使用对象名进行访问。当然,鉴于他作用的特殊性更推荐用类名访问~~
使用 static 可以修饰变量、方法和代码块。
常量
final关键字
调用常量用类名.常量名
什么是java
java划分为三个技术平台,javaSE(平台标准版,普通桌面和商务应用程序),javaEE(平台企业版),javaME(平台微型版,开发电子消费产品,嵌入式设备)
1.JDK
.javac 是编译文件,.java是运行文件
java开发环境简称JDK,包括java编译器,java运行工具,文档生成工具,java打包工具
java运行环境简称JRE,JRE中只有java运行工具*********
JVM java虚拟机,Java程序需要运行在虚拟机上,不同的平台有自己的虚拟机,因此Java语言可以实现跨平台
jdk包含jre和开发工具,jre包含java基础类库和jvm
系统环境变量,JAVA-HOME,CLASSPATH(只对类文件有效)
java运行机制
.java的源文件-----编译器(JDK)----- .class的字节码文件------解析器(JVM)----机器码文件----计算机
一个java文件有多个类,一个类生成一个.class文件
java程序结构至少包含一个类,只能有一个public,如果有public,类名和java文件名一样
java 基本语法
- java程序分为结构定义语句和功能执行语句,结构定义声明一个类或方法,功能执行用于实现具体的功能
2.java语言的特点
简单易用,安全可靠,跨平台,面向对象,支持多线程,动态的,高性能,可移植
Java即是编译型的,也是解释型语言,总的来说Java更接近解释型语言
@[TOC]java开发的基础语法
二:java程序开发的基础语法***********************************************
命名规范
- 标识符:
给包,类,方法,变量起名字的符号
标识符以字母,下划线,美元符号开头
其他的部分可以是字母,下划线,美元符号,和数字组成
不可以是java关键字
不能含有空格
不能和jdk中的名字一样 - 包名:全部小写,多级包用.隔开。,域名倒着写
举例:com.jourwon - 类、接口:大驼峰命名
一个单词首字母大写,多个单词每个单词的首字母大写。
举例:Student,Car,HelloWorld - 方法和变量:小驼峰命名
一个单词首字母小写,多个单词从第二个单词开始每个单词的首字母大写。
变量可以用汉字命名
举例:age,maxAge,show(),getAge() - 常量:如果是一个单词,所有字母大写,如果是多个单词,所有的单词大写,用下划线区分每个单词。
举例:DATE,MAX_AGE - 项目名:全部用小写字母,多个单词之间用横杆-分割。
举例:demo,spring-boot
true,false和,null看起来像关键字,但它们实际上是文字; 您不能在程序中将它们用作标识符,
保留字:除了关键字种的保留字,还有,true,false,null.
语法规范
print/println的输出原则
在遇到第一个字符/字符串之前
所有的数遇到加号即相加
当遇到任意字符后
加号就变成了字符/字符串的拼接
注释
- 单行注释
格式: // 注释文字 - 多行注释
格式: /* 注释文字 */ - 文档注释
格式:/** 注释文字 */
多行注释可以嵌套单行注释,不可以嵌套多行注释
3. java修饰符(权限)
- private : 在同一类内可见。使用对象:变量、方法。 注意:不能修饰类(外部类)
- default (即缺省,什么也不写,不使用任何关键字): 在同一包内可见,不使用任何修饰符。使用对象:类、接口、变量、方法。
- protected : 对同一包内的类和所有子类可见。使用对象:变量、方法。 注意:不能修饰类(外部类)。
- public : 对所有类可见。使用对象:类、接口、变量、方法
4. 分隔符
定义
- 空格,逗号,分号以及结束符都被称为分隔符,规定任意两个相邻标识符、数字、保留字或语句之间必须至少有一个分隔符,以便程序编译时能够识别。
- 必须是半角下的英文符号
分类
- 分号,用来终止一个语句
注释
- 多行注解和文档注解不能嵌套
5. java八种基本数据类型
单引号的字符型中必须有字符,空格也算。双引号的字符串可以为空
基本数据类型
数值型
- java整数类型byte,short,int,long
- 浮点类型float,double
字符型 - char
布尔类型 - boolean,只能允许true和false,无null,不可以用0或非0代替,这点和C语言不同
- 浮点数常量前后的数字不能省略
基本数据类型的转换
红色虚线表示,转换之后可能会导致精度损失
6. 运算符
算数运算符
+当String字符串与其他数据类型只能做连接运算,结果为String类型
/除法,两个数都是int结果是int,有一个数有小数点,结果就是小数
比较运算符(关系运算符)
赋值运算符
=,+=,-=,*=,/=,%=
+=: a+=20;相当于a = (a的数据类型)(a + 20);
逻辑运算符
“&” 和"&&" 的区别;
“&”,左边无论真假,右边都进行计算。
只有全为true时为真
一真一假为假
“&&”,左边为假,右边不进行计算。
只有全真的时候为真
一真一假为假
“|“和”||” 的区别;
“|”,左边无论真假,右边都进行计算。
全真为真,全假为假,一真一假全为真
“||”,左边为真,右边不进行计算。
同上
^ 逻辑异或,左右都为true时,结果为false,一真一假,结果为true
位运算符
三元运算符(三目运算符)
- 运算后结果的表达式类型必须一致
- 一定可以使用if…else代替,反之不一定成立
位运算符
运算符优先级
7. 表达式
用运算符把常量或者变量连接起来符号java语法的式子就可以称为表达式
表达式值的数据类型即为表达式的类型
表达式不带分号
- a + b
3.14 + a
(x + y) * z + 100
boolean b= i < 10 && (i%10 != 0)
8. java流程控制语句
流程是指程序运行时,各语句的执行顺序。流程控制语句就是用来控制程序中各语句执行的顺序
顺序结构
根据代码的顺序依次执行
分支结构(选择结构)
- 定义
条件语句可根据不同的条件执行不同的语句。包括if条件语句与switch多分支语句。 - 分类
if…else分支结构
switch分支结构
switch(控制表达式)
控制表达式是整型和字符串
循环结构
- 定义
循环语句就是在满足一定条件的情况下反复执行某一个操作。包括while循环语句、do···while循环语句和for循环语句。
while循环语句
括号里的循环条件应该是Boolean类型
do…while循环语句
for循环语句
continue的使用场景:在循环语句中
continue的作用:结束一次循环,继续下一次的循环
注意:离开使用场景的存在是没有意义的
continue与break区别:
break 退出当前循环
continue 退出本次循环
9. IO流
初始IO流
流,一个抽象的概念,是指一连串的数据(字符或者字节),是以先进先出的方式发送信息的通道。
当程序需要读取数据的时候,就会开启一个通向数据源的流,这个数据源可以是文件,内存,或是网络连接。类似的,当程序需要写入数据的时候,就会开启一个通向目的地的流。这时候你就可以想象数据好像在这其中“流”动一样。
特点
- 先进先出,最先写入输出流的数据最先被输入流读取到
- 顺序存取,可以一个一个的写入字节,读取时将按照写入的顺序读取,不能随机访问中间的数据(RandomAccessFile除外)
- 只读或只写,没一个流只能是输入或者输出流的一种,不能同时具备两个功能,输入流只能进行读,输出流只能进行写,在一个数据传输通道中,如果既要写数据,又要读数据,则分别提供两个流
标题
IO流主要分类方式有:
1. 按数据流的方向:输入流,输出流
输入与输出是相对于应用程序而言的,比如文件读写,读取文件是输入流,写文件是输出流,这点很容易搞反
2. 按处理数据单位:字节流,字符流
字节流和字符流的操作单元不同,字节流操作8位的字节,字符流操作16位字符
字节流可以处理一切文件,字符流只能处理纯文本文件
字节流本身没有缓冲区,缓冲字节流相对于字节流,效率提升非常高,字符缓冲流提升并不高
3. 按功能:字节流,处理流
节点流:直接操作数据读写的流类,比如FileInputStream
处理流:对一个已存在的流的链接和封装,通过对数据进行处理为程序提供功能强大、灵活的读写功能,例如BufferedInputStream(缓冲字节流)
处理流是对节点流的封装,最终的数据处理还是由节点流完成的。
缓冲流,就应用这种思路:普通流每次读写一个字节,而缓冲流在内存中设置一个缓存区,缓冲区先存储足够的待操作数据后,再与内存或磁盘进行交互。这样,在总数据量不变的情况下,通过提高每次交互的数据量,减少了交互次数。
转换流
InputStreamReader:字节输入流转换为字符输入流
InputStreamReader i=new InputStreamReader(new FileInputStream("rea.txt"))
OutputStreamWriter:字节输出流转换为字符输出流
OutputStreamWriter i=new OutputStreamWriter(new FileOutputStream("w.txt"))
java数组
一个数组可以存放不同类型的数值
一维数组
数据类型[] 数组名称 = new 数据类型 [长度] ;
数据类型[] 数组名称 = new 数据类型 [] {数组元素0,数组元素1…};
数组类型[] 数组名称={数组元素0,数组元素1…};
int[] a; //声明一个int[]类型的变量,存放在栈内存,
a=new int[100]; //创建一个长度为100的数组,并将数组地址赋给数组类型的变量x,这个时候还没有初始值,存放在堆内存
用new创建的有初始值,
二维数组
二维数组中,a.length表示行数,,a[i].length表示第i行的列数
int[][] a = new int[2][3];
int[][] a = new int[2][];
**int[] a[];**这个也对
后面可以省略,前面不可省略
int[][] a = {{1},{1,2},{1,2,3}};
冒泡排序
简单来说,冒泡排序就是重复地走访过要排序的数列,一次比较两个元素,如果他们的顺序错误就把他们交换过来。走访数列的工作是重复地进行直到没有再需要交换,也就是说该数列已经排序完成。
package day0515;
public class demo_sort {
public static void main(String[] args) {
//冒泡排序算法
int[] numbers=new int[]{1,5,8,2,3,9,4};
//需进行length-1次冒泡
for(int i=0;i<numbers.length-1;i++)
{
for(int j=0;j<numbers.length-1-i;j++)
{
if(numbers[j]>numbers[j+1])
{
int temp=numbers[j];
numbers[j]=numbers[j+1];
numbers[j+1]=temp;
}
}
}
System.out.println("从小到大排序后的结果是:");
for(int i=0;i<numbers.length;i++)
System.out.print(numbers[i]+" ");
}
}
选择排序
先找到最小元素所在位置的索引,然后将该元素与第一位上的元素进行交换。
public static void order2(int[] a) {
for (int i = 0; i < a.length-1; i++) {
for (int j = i + 1; j < a.length; j++) {
if (a[i] > a[j]) {
int t = a[i];
a[i] = a[j];
a[j] = t;
}
}
}
}
面向对象
类和对象的关系
类是对某一类事物的抽象描述,
人是类,每个具体的人(张三,李四。。。)看做对象,
对象是类的实例,是类的具体化
类是对象的模板,是描述多个对象的共同特征
方法的重载
方法的名称相同,参数的类型和个数不同
与返回值类型无关
覆盖和重载的不同
重载与覆盖的区别
1、方法的覆盖是子类和父类之间的关系,是垂直关系;方法的重载是同一个类中方法之间的关系,是水平关系
2、覆盖只能由一个方法,或只能由一对方法产生关系;方法的重载是多个方法之间的关系
3、覆盖要求参数列表相同;重载要求参数列表不同
4、覆盖关系中,调用那个方法体,是根据对象的类型(对象对应存储空间类型)来决定;重载关系,是根据调用时的实参表与形参表来选择方法体的
方法的递归
在一个方法的内部调用自身的过程,递归必须有结束条件,不然就会陷入无限递归的状态,永远无法结束调用。
面向对象三大特征:封装,继承,多态
封装性: 安全性和重用性。
继承性: 高效性和重用性。
多态性: 统一性(有机性)和高效性。
一、面向对象概述
在面向对象定义之中,也规定了一些基本的特征:
(1) 封装:
1、封装的概念:
将类的某些信息隐藏在类的内部,不允许外部程序直接访问,而是通过该类提供的方法来对隐藏的信息进行操作和访问。
2、好处:
(1)只能通过规定的方法访问数据
(2)隐藏类的实例细节,方便修改和实现。
3、封装的实现步骤
(1)修改属性的可见性设为(private)
(2)创建getter/setter方法(用于属性的读写)(通过这两种方法对数据进行获取和设定,对象通过调用这两种发方法实现对数据的读写)
(3)在getter/setter方法中加入属性控制语句(对属性值的合法性进行判断)
属性访问
package edu.sandtower.bean;
public class Person{
// 声明公开属性
public String name;
// 声明私有属性
private double money;
}
package edu.sandtower.test;
import edu.sandtower.bean.Person;
public class Test{
public static void main(String[] args){
// 在test包中的Test类中创建Person实例
Person person = new Person();
person.name = "小张";// 编译通过,可以访问name属性
person.money = 500.0;// 编译失败,无法访问money属性
}
}
封装实例
public class Person{
public String name;
public double money;
}
public class Test{
public static void main(String[] args){
Person person = new Person();
person.money = 500;// 初始金额500元
System.out.println("购买一张桌子,花费200元");
person.money -= 200;
System.out.println("购买二手PSP,花费350元");
person.money -= 350;
System.out.println("目前余额为:" + person.money);// -50
}
}
可以看到,经过代码操作以后可能会导致money的属性为负。看官甲:你自己不加判断赖代码?没错,这个问题我们可以增加判断代码来解决,由于这个操作是对money属性值的操作,我们将它封装成一个方法写在实体类中,于是有了改进之后的代码:
public class Person{
public String name;
public double money;
// 定义一个方法,用于设置money属性的值
public void setMoney(double money){
if(money >= 0){
this.money = money;
}
}
}
public class Test{
public static void main(String[] args){
Person person = new Person();
person.money = 500;// 初始金额500元
System.out.println("购买一张桌子,花费200元");
person.setMoney(person.money - 200);
System.out.println("购买二手PSP,花费350元");
person.setMoney(person.money - 350);
System.out.println("目前余额为:" + person.money);// 300
}
}
经过上面的改进,我们可以确保money的值不为负数,同时可以看到,当在实体类中定义方法后,使用者需要修改属性值时直接调用方法就可以保证不出问题。但是由于属性值依然可以被直接访问,还不能保证万无一失,于是我们利用权限修饰符使得变量不能被直接访问,同时需要定义一个能够取得属性值的方法。
public class Person{
public String name;
// 声明money属性为private权限
private double money;
// 定义一个方法,用于设置money属性的值
public void setMoney(double money){
if(money >= 0){
this.money = money;
}
}
// 定义一个方法,用于获取money属性的值
public double getMoney(){
return this.money;
}
}
public class Test{
public static void main(String[] args){
Person person = new Person();
person.setMoney(500);// 初始金额500元,此时已经不能使用对象.属性的方法赋值
System.out.println("购买一张桌子,花费200元");
person.setMoney(person.getMoney() - 200);
System.out.println("购买二手PSP,花费350元");
person.setMoney(person.getMoney() - 300);
System.out.println("目前余额为:" + person.getMoney());// 300
}
}
总结:如何进行封装
在进行封装时都是出于对属性保护的考虑,可以按照以下两个步骤来进行:
使用权限修饰符
使用private作用在属性上,关闭直接访问的入口
使用public作用在方法上,提供调用的入口
定义与属性存取相关的方法
在属性关闭后,我们需要通过方法来获取属性的值以及对属性值进行修改。由于有了方法结构,我们就可以对存入的数据进行判断,对不符合逻辑的数据进行处理。
(2) 继承
父类 对象=new 子类();
:在原本的基础之上继续进行扩充;继承是类与类之间的联系,知道一个类,该类描述了基础属性和功能,另一个类不仅包含了该类的属性和功能,还增加了自己的属性和功能,增强了复用性,提高开发效率
重写父类的方法
子类重写的方法要和父类的方法名,参数,返回值类型相同,并且访问权限要大于等于父类的(指的是方法,不是类)
final关键字
final修饰的类不能被继承
修饰的方法不能被子类重写
修饰的变量(成员变量和局部变量)是常量,只能赋值一次(不能修改)
super关键字
当子类重写父类中的方法后,子类对象无法直接访问父类被重写的方法。用super调用父类中的成员变量和成员方法
super.成员变量
super.成员方法(参数1,参数2,..........)
调用构造方法
super([参数1,参数2,.........])
需要注意的是,通过super调用父类构造方法的代码必须位于子类构造方法的第一行,并且只能出现一次,否则报错
class animal{
//定义动物叫的方法
void shout(){
String name="动物";
System.out.println("动物发出叫声");
}
}
//定义类继承animal类
class dog extends animal{
String name="犬类";
//重写父类的shout方法
void shout(){
super.shout();//访问父类中的成员方法
}
void printname(){
System.out.println(super.name);//访问父类的成员变量
}
}
public class Test2{
public static void main(String[] args)
{
dog d=new dog();
dog.shout();//调用d对象重写的shout方法
dog.printname();//调用d对象的printname方法
}
}
运行结果
动物发出叫声
name=动物
继承的限制
- 子类对象在进行实例化前首先调用父类中的构造方法,再调用子类构造方法实例化子类对象
class Person {
private String name;
private int age;
public String getName()
{
return name;
}
public void setName(String name)
{
this.name=name;
}
public int getAge()
{
return age;
}
public void setAge(int age)
{
this.age=age;
}
//构造方法
public Person()
{
System.out.println("父类的构造方法");
}
}
class Student extends Person{
private String schoolName;
public String getSchoolName()
{
return schoolName;
}
public void setSchoolName(String schoolName)
{
this.schoolName=schoolName;
}
//构造方法
public Student()
{
System.out.println("子类的构造方法");
}
}
public class Test2{
public static void main(String[] args)
{
Student student=new Student();
student.setName("花花");
student.setAge(18);
student.setSchoolName("励志中学");
System.out.println("学校:"+student.getSchoolName() +" 姓名:"+student.getName()+" 年龄:"+student.getAge());
}
}
实际在子类构造方法中,相当于隐含了一个语句super(),调用父类的无参构造。同时如果父类里没有提供无参构造,那么这个时候就必须使用super(参数)明确指明要调用的父类构造方法。
- Java只允许单继承不允许多继承(一个子类继承一个父类)`
class A{
}
class B extends A{
}
class C extends B{
}
构造方法的继承
class A{
public A()
{
System.out.println("1.A类的构造方法");
}
{
System.out.println("2.A类的构造块");
}
static{
System.out.println("3.A类的静态方法");
}
}
public class B extends A{
public B()
{
System.out.println("4.B类的构造方法");
}
{
System.out.println("5.B类的构造块");
}
static{
System.out.println("6.B类的静态方法");
}
public static void main(String[] args)
{
System.out.println("7.start......");
new B();
new B();
System.out.println("8.end.....");
}
}
主类中的静态块优先于主方法执行:只在程序启动后执行一次,优先级最高,所以6应该在7前面执行,但是B类继承于A类,所以先执行A类的静态块3,所以进主方法前的执行顺序为:3 6,进主方法后执行7,new B()之后应先执行A的构造方法然后执行B的构造方法,但由于A类和B类均有构造块,构造块又优先于构造方法执行即 2 1(A的构造家族) 5 4(B的构造家族),有多少个对象,构造家族就执行几次,题目中有两个对象 所以执行顺序为:3 6 7 2 1 5 4 2 1 5 4 8
在进行继承的时候,子类会继承父类的所有结构(包括私有属性、构造方法、普通方法)
继承方式
显示继承:所有非私有操作属于显示继承(可以直接调用)。
隐式继承:所有私有操作属于隐式继承(不可以直接调用,需要通过其它形式调用(get或者set))。
class Person {
private String name;
private int age;
public String getName()
{
return name;
}
public void setName(String name)
{
this.name=name;
}
public int getAge()
{
return age;
}
public void setAge(int age)
{
this.age=age;
}
public Person()
{
System.out.println("父类的构造方法");
}
public void fun()
{
System.out.println("好好学习。");
}
}
class Student extends Person{
private String schoolName;
public String getSchoolName()
{
return schoolName;
}
public void setSchoolName(String schoolName)
{
this.schoolName=schoolName;
}
public Student()
{
System.out.println("子类的构造方法");
}
}
public class Test2{
public static void main(String[] args)
{
Student student=new Student();
student.setName("花花");
student.setAge(18);
//隐式继承
System.out.println("姓名:"+student.getName()+" 年龄:"+student.getAge());
//显示继承
student.fun();
}
}
运行结果
父类的构造方法
子类的构造方法
姓名:花花 年龄:18
好好学习
抽象类
抽象类必须有有子类
- 当一个类中包含了抽象方法,那么该类也必须使用abstract关键字来修饰,这种使用abstract关键字修饰的类就是抽象类
- 需要注意的是,包含抽象方法的类必须定义为抽象类,但是抽象类可以不包含任何抽象方法,
- 另外,抽象类是不可以被实例化的, 因为抽象类中可能包含抽象方法,抽象方法没有方法体(大括号以及大括号中的内容),不可以被调用, 如果想调用,需要创建一个子类,在子类中实现抽象类中的抽象方法子类必须覆写抽象类之中的全部抽象方法(如果子类没有实现父类的抽象方法,则必须将子类也定义为abstract类)
抽象方法必须为public或者protected(因为如果为private,则不能被子类继承,子类便无法实现该方法),缺省情况下默认为public;
【修饰符】abstract class 类名{
【修饰符】 abstract 方法返回值类型 方法名(参数列表);
}
public abstract void shout();
抽象类中的构造方法存在的目的是为了属性的初始化
任何时候,如果要执行类中的static方法的时候,都可以在没有对象的情况下直接调用,对于抽象类也一样。
package com.wz.abstractdemo;
abstract class A{//定义一个抽象类
public void fun(){//普通方法
System.out.println("存在方法体的方法");
}
public abstract void print();//抽象方法,没有方法体,有abstract关键字做修饰
}
//单继承
class B extends A{//B类是抽象类的子类,是一个普通类
@Override
public void print() {//强制要求覆写
System.out.println("Hello World !");
}
}
public class TestDemo {
public static void main(String[] args) {
A a = new B();//向上转型
a.print();//被子类所覆写的过的方法
}
}
现在就可以清楚的发现:
(1)抽象类继承子类里面有明确的方法覆写要求,而普通类可以有选择性的来决定是否需要覆写;
(2)抽象类实际上就比普通类多了一些抽象方法而已,其他组成部分和普通类完全一样;
(3)普通类对象可以直接实例化,但抽象类的对象必须经过向上转型之后才可以得到。
抽象类的限制
(1)抽象类中有构造方法么?
由于抽象类里会存在一些属性,那么抽象类中一定存在构造方法,其存在目的是为了属性的初始化。
并且子类对象实例化的时候,依然满足先执行父类构造,再执行子类构造的顺序。
范例如下:
package com.wz.abstractdemo;
abstract class A{//定义一个抽象类
public A(){
System.out.println("*****A类构造方法*****");
}
public abstract void print();//抽象方法,没有方法体,有abstract关键字做修饰
}
//单继承
class B extends A{//B类是抽象类的子类,是一个普通类
public B(){
System.out.println("*****B类构造方法*****");
}
@Override
public void print() {//强制要求覆写
System.out.println("Hello World !");
}
}
public class TestDemo {
public static void main(String[] args) {
A a = new B();//向上转型
}
}
执行结果
*****A类构造方法*****
*****B类构造方法*****
(2)抽象类可以用final声明么?
不能,因为抽象类必须有子类,而final定义的类不能有子类;
(3)抽象类能否使用static声明?
先看一个关于外部抽象类的范例:
package com.wz.abstractdemo;
static abstract class A{//定义一个抽象类
public abstract void print();
}
class B extends A{
public void print(){
System.out.println("**********");
}
}
public class TestDemo {
public static void main(String[] args) {
A a = new B();//向上转型
a.print();
}
}
执行结果:报错
再看一个关于内部抽象类:
package com.wz.abstractdemo;
abstract class A{//定义一个抽象类
static abstract class B{//static定义的内部类属于外部类
public abstract void print();
}
}
class C extends A.B{
public void print(){
System.out.println("**********");
}
}
public class TestDemo {
public static void main(String[] args) {
A.B ab = new C();//向上转型
ab.print();
}
}
运行结果:正确
由此可见,外部抽象类不允许使用static声明,而内部的抽象类运行使用static声明。使用static声明的内部抽象类相当于一个外部抽象类,继承的时候使用“外部类.内部类”的形式表示类名称。
接口
接口不能被实例化,不能有构造方法
接口与接口之间只能有继承关系
- 接口是一个特殊的抽象类,不能包含普通方法,其内部的所有方法都是抽象方法
- 接口中除了抽象方法外,还可以有默认方法和静态方法(也叫类方法),默认方法使用的default修饰,静态方法用static修饰,并且这两种方法都允许有有方法体
- 定义接口时不再使用class关键字
- 修饰符 interface 接口名 (extends 父接口1,父接口2,…){
- 接口中所有的成员属性都是public static final修订的常量
public static final 常量类型 常量名=常量值; - 接口中所有的方法都是public abstract修订的
public abstract 方法返回值类型 方法名(参数列表);
}
public default 方法返回值类型 方法名(参数列表){
//默认方法的方法体
}
public static 方法返回值类型 方法名(参数列表){
//静态方法的方法体
}
implements:实现接口
- 静态方法可以通过“接口.方法名”的形式调用,而抽象方法和默认方法只能通过接口实现类的实例对象来调用
一个类可以在继承另一个类的同时实现多个接口
(3)多态:
指不同类的对象在调用同一个方法时所呈现出的多种不同行为,多态性是由类的继承,方法重写以及父类引用指向子类对象体现的
通过多态,消除了类之间的耦合关系,大大提高了程序的的可拓展性和可维护性
在一个指定的范围之内进行概念的转换。在一个类中定义的属性和功能被其他类继承后,当子类对象直接赋值给父类引用变量时,相同引用数据类型的变量调用同一个方法所诚信啊出的多种不同行为特征
对于面向对象的开发来讲也分为三个过程:OOA(面向对象分析)、OOD(面向对象设计)、OOP(面向对象编程)。
多态的定义与使用格式
定义格式:父类类型 变量名=new 子类类型();
多态中的成员变量:编译运行看左边
Fu f=new Zi();
System.out.println(f.num);//f是Fu中的值,只能取到父中的值
多态中的成员方法:编译看左边,运行看右边
Fu f1=new Zi();
System.out.println(f1.show());//f1的门面类型是Fu,但实际类型是Zi,所以调用的是重写后的方法。
instanceof关键字
判断一个对象是否为某个类(或接口)的势力或者子类实例
- 注意:返回值类型是boolean类型
Fu f1=new Zi();
Fu f2=new Son();
if(f1 instanceof Zi){
System.out.println("f1是Zi的类型");
}
else{
System.out.println("f1是Son的类型");
}
多态的转型
-
向上转型:
使用格式:父类类型 变量名=new 子类类型(); 适用场景:当不需要面对子类类型时,通过提高扩展性,或者使用父类的功能就能完成相应的操作。
-
向下转型:一个已经向上转型的子类对象可以使用强制类型转换的格式,将父类引用类型转为子类引用各类型
使用格式:子类类型 变量名=(子类类型) 父类类型的变量; 适用场景:当要使用子类特有功能时。
多态案例
例如: Animal 是父类, Dog是子类, 而getName()方法是Dog子类特有的方法。
因此, 当有 Anaimal a = new Dog(); 时,
则 a.getName(); 是否正确?
答: 错误。
问: 那如何访问?
答: 需要先强制转换(向下转型)
子类类名 对象变量 = (子类类名)父类的对象变量;
即: Dog d = (Dog)a;
之后, d.getName(); 此时,正确。
package day0524;
public class demo04 {
public static void main(String[] args) {
People p=new Stu();
p.eat();
//调用特有的方法
Stu s=(Stu)p;
s.study();
//((Stu) p).study();
}
}
class People{
public void eat(){
System.out.println("吃饭");
}
}
class Stu extends People{
@Override
public void eat(){
System.out.println("吃水煮肉片");
}
public void study(){
System.out.println("好好学习");
}
}
class Teachers extends People{
@Override
public void eat(){
System.out.println("吃樱桃");
}
public void teach(){
System.out.println("认真授课");
}
}
二、类与对象的基本概念
类与对象是整个面向对象中最基本的组成单元
类:是抽象的概念集合,表示的是一个共性的产物,类之中定义的是属性和行为(方法);
对象: 对象是一种个性的表示,表示一个独立的个体,每个对象拥有自己独立的属性,依靠属性来区分不同对象。
可以一句话来总结出类和对象的区别:类是对象的模板,对象是类的实例。类只有通过对象才可以使用,而在开发之中应该先产生类,之后再产生对象。类不能直接使用,对象是可以直接使用的。
三、类和对象的定义和使用
定义类
[修饰符] class 类名称 {
属性 (变量) ;
行为 (方法) ;
}
//定义一个Person类
class Person { // 类名称首字母大写
String name ;//声明类的成员变量,声明变量
private int age=20;//赋值变量
public void tell() { // 没有static,定义一个成员方法
System.out.println("姓名:" + name + ",年龄:" + age) ;
}
}
定义在类中的变量称为成员变量,定义在方法中的变量称为局部变量,如果在某一个方法中定义的局部变量和成员变量同名,此时通过变量名访问到的是局部变量,而不是成员变量 P81
声明和实例化对象
类名称 对象名称 = new 类名称 () ;
引用数据类型与基本数据类型最大的不同在于:引用数据类型需要内存的分配和使用。所以,关键字new的主要功能就是分配内存空间,也就是说,只要使用引用数据类型,就要使用关键字new来分配内存空间。
当一个实例化对象产生之后,可以按照如下的方式进行类的操作:
对象.属性: 表示调用类之中的属性;
对象.方法(): 表示调用类之中的方法。
package com.wz.classandobj;
class Person {
String name ;
int age ;
public void get() {
System.out.println("姓名:" + name + ",年龄:" + age);
}
}
public class TestDemo {
public static void main(String args[]) {
Person per = new Person() ;// 声明并实例化对象
per.name = "张三" ;//操作属性内容
per.age = 30 ;//操作属性内容
per.get() ;//调用类中的get()方法
}
}
//运行结果:姓名:张三,年龄:30
访问控制符,修饰符权限
构造方法
不能重写但是可以重载,因为构造方法不能被继承
条件
- 方法与类名相同
- 没有返回值类型的声明
- 方法中不能使用return语句返回一个值,可以单独写return语句来作为方法的结束
- 如果存在有参的构造方法,则系统不在提供无参的构造方法
- 为了方便,一般使用public修饰
- 构造方法调用
Person person=new Person(参数1,参数2...)
this关键字
使用this调用类的构造方法时,应注意:
- 只能在构造方法中使用this调用其他的构造方法,不能再成员方法中使用
- 在构造方法中,使用this调用构造方法的语句必须是该方法的第一条并且只能出现一次
- 不能在一个类的两个构造方法中使用this互相调用
class person{
int age;//成员变量
println("无参函数被调用");
public person(int age){//局部变量age
this.age=age;//将局部变量age的值赋给成员变量age
this();//调用无参的构造方法
println("有参函数被调用");
}
//通过this关键字调用成员方法
public void open(){
...............
}
public void speak(){
this.open();//调用open方法
}
}
public static void main(String[] args){
person p=new person(18);
输出:无参函数被调用
有参函数本调用
this()先调用了无参构造方法
}
static关键字(静态变量,方法,代码块)
数据共享
修饰类的成员,如成员变量,成员方法,代码块等
静态变量调用:类名.变量名
static关键字只能修饰成员变量,不能修饰局部变量
静态方法调用:类名.方法
或者实例对象名.方法
1、 静态方法中可以直接调用同类中的静态成员,但不能直接调用非静态成员,如果希望在静态方法中调用非静态变量,可以通过创建类的对象,然后通过对象来访问非静态变量。
2、 在普通成员方法中,则可以直接访问同类的非静态变量和静态变量,
在一个静态方法中只能访问用static修饰的成员
静态代码块:static{
}
静态代码块在类被加载时就会执行,由于类只加载一次,所以静态代码块只执行一次(只在编译的时候执行)
Java 中的 static 使用之静态初始化块
在编辑器中,分别通过构造方法、初始化块和静态初始化块对变量进行初始化赋值:
public class HelloWorld {
String name; // 声明变量name
String sex; // 声明变量sex
static int age;// 声明静态变量age
// 构造方法
public HelloWorld () {
System.out.println("通过构造方法初始化name");
name = "tom";
}
// 初始化块
{
System.out.println("通过初始化块初始化sex");
sex = "男";
}
// 静态初始化块
static {
System.out.println("通过静态初始化块初始化age");
age = 20;
}
public void show() {
System.out.println("姓名:" + name + ",性别:" + sex + ",年龄:" + age);
}
public static void main(String[] args) {
// 创建对象
HelloWorld hello = new HelloWorld();
// 调用对象的show方法
hello.show();
}
}
内部类
将一个类放到另一个类的内部,就是内部类
分为四种:成员内部类,静态内部类,局部(方法)内部类,匿名内部类
内部类的定义
class Outer{
private String str ="外部类中的字符串";
//**************************
//定义一个内部类
class Inner{
private String inStr= "内部类中的字符串";
//定义一个普通方法
public void print(){
//调用外部类的str属性
System.out.println(str);
}
}
//**************************
//在外部类中定义一个方法,该方法负责产生内部类对象并调用print()方法
public void fun(){
//内部类对象
Inner in = new Inner();
//内部类对象提供的print
in.print();
}
}
public class Test{
public static void main(String[] args)
{
//创建外部类对象
Outer out = new Outer();
//外部类方法
out.fun();
}
}
运行结果:外部类中的字符串
内部类的优缺点
-
内部类优点
内部类与外部类可以方便的访问彼此的私有域(包括私有方法,私有属性)
内部类是另外一种封装,对外部的其他类隐藏
内部类可以实现java的单继承局限 -
内部类缺点
结构复杂
创建内部类
在外部类外部,创建非静态内部类
语法:外部类.内部类 内部类对象 = new 外部类().new 内部类();
举例:Outer.Inner in = new Outer().new Inner();
外部类外部,创建静态内部类
语法:外部类.内部类 内部类对象 = new 外部类.内部类();
举例:Outer.Inner in = new Outer.Inner();
在外部类内部创建内部类
在外部类内部创建内部类,就像普通对象一样直接创建:Inner in = new Inner();
成员内部类
成员内部类可以访问外部类的所有成员,同时外部类也可以访问成员内部类的所有成员
内部访问外部:直接访问或者对象名.方法名
外部访问内部:只能对象名.方法名
1、Inner类定义在Outer类的内部,相当于Outer类的成员变量的位置,Inner类可以使用任意访问修饰符,如:public、private、protected等。
2、Inner类中定义的test()方法可以访问Outer类中的数据,不受访问控制符的影响。
3、 定义了成员内部类后,必须使用外部类对象来创建内部类对象,而不能直接去 new 一个内部类对象,即:内部类 对象名 = 外部类对象.new 内部类( );如创建Inner的内部类对象:要先创建外部类对象:Outer o = new outer(); 创建内部类:Inner i = o.new Inner();访问Inner中的test()方法:i.test();
4、如果外部类和内部类具有相同的成员变量或方法,内部类可以直接访问内部类的成员变量或方法,但如果内部类访问外部类的成员变量或者方法时,需要使用this关键字;如下:
class Outer {
private String name = "test";
public static int age =20;
class Inner{
public static int num =10;
public void fun()
{
System.out.println(name);
System.out.println(age);
}
}
}
public class Test{
public static void main(String [] args)
{}
}
局部内部类
定义在某个局部范围中的类**(定义在外部类的方法中,只能在该方法中使用),可以访问外部类的所有成员变量和方法**有效范围只限于方法内部
- 方法内部类不允许使用访问权限修饰符(public、private、protected)均不允许。
class Outer{
private int num =5;
public void dispaly(final int temp)
{
//方法内部类即嵌套在方法里面
public class Inner{
}
}
}
public class Test{
public static void main(String[] args)
{}
}
-
方法内部类对外部完全隐藏,除了创建这个类的方法可以访问它以外,其他地方均不能访问 (换句话说其他方法或者类都不知道有这个类的存在)
-
方法内部类如果想要使用方法形参,该形参必须使用final声明(JDK8形参变为隐式final声明)
-
可以访问外部类的所有成员变量和方法,但是局部内部类中的变量和方法只能在创建该局部内部类的方法中进行
匿名内部类
匿名内部类就是一个没有名字的方法内部类,因此特点和方法与方法内部类完全一致,除此之外,还有自己的特点:
1.匿名内部类必须继承一个抽象类或者实现一个接口。
2.匿名内部类没有类名,因此没有构造方法。
new 父接口(){
//匿名内部类实现部分
}
//匿名内部类
//声明一个接口
interface MyInterface {
//接口中方法没有方法体
void test();
}
class Outer{
private int num = 5;
public void dispaly(int temp)
{
//匿名内部类,匿名的实现了MyInterface接口
//隐藏的class声明
new MyInterface()
{
public void test()
{
System.out.println("匿名实现MyInterface接口");
System.out.println(temp);
}
}.test();
}
}
public class Test{
public static void main(String[] args)
{
Outer out = new Outer();
out.dispaly(3);
}
}
静态内部类
使用static修饰的内部类我们称之为静态内部类
- 静态内部类的创建不需要依赖外部类,可以直接创建
- 静态内部类只能访问外部类的静态成员,可以存在自己的成员变量。
- 通过外部类访问静态内部类成员时,可以跳过外部类从而直接通过内部类访问静态内部类成员
内部类和外部类的关系
对于非静态的内部类,内部类的创建依赖外部类的实例对象,在没有外部类实例之前是无法创建内部类的。
内部类可以直接访问外部类的元素(包括私有域)—外部类在内部类之前创建,创建内部类时会将外部类的对象传入
class Outer{
//成员变量 与对象有关
private String msg;
private int age;
//--------------------------
class Inner{
public void dispaly()
{
//此处有一个隐藏的Outer.this
msg = "test";
age = 20;
System.out.println(msg);
System.out.println(age);
}
}
//--------------------------
public void test()
{
Inner in = new Inner();
in.dispaly();
}
}
public class Test{
public static void main(String[] args)
{
Outer out = new Outer();
out.test();
}
}
外部类可以通过内部类的引用间接访问内部类元素 – -要想访问内部类属性,必须先创建内部类对象
class Outer{
public void dispaly()
{
//外部类通过创建内部类的对象间接访问内部类元素
Inner in = new Inner();
in.dispaly();
}
class Inner{
public void dispaly()
{
System.out.println("内部类");
}
}
}
public class Test1{
public static void main(String[] args)
{
Outer out = new Outer();
out.dispaly();
}
}
内部类是一个相对独立的个体,与外部类没有关系
匿名对象
没有名字的对象
new Car(); //匿名对象其实就是定义对象的简写格式。
正常变量
Car c = new Car();
c.run();
用匿名对象来书写以上代码:
new Car().run();
匿名对象执行完每一条语句之后,由于再无引用引用之,java自动回收机制会视作垃圾处理
java异常
在 Java 中,所有的异常都有一个共同的祖先 Throwable(可抛出)。Throwable 指定代码中可用异常传播机制通过 Java 应用程序传输的任何问题的共性。
Error(错误)与Exception(异常)区别:错误不能处理,异常可以由程序本身处理
但是在exception类中有一个子类RuntimeException类,该类及其子类用于表示运行时异常,除此之外均表示编译异常
Throwable类中常用方法如下:
1. 返回异常发生时的详细信息
public string getMessage();
2. 返回异常发生时的简要描述
public string toString();
3. 返回异常对象的本地化信息。使用Throwable的子类覆盖这个方法,可以声称本地化信息。如果子类没有覆盖该方法,则该方法返回的信息与getMessage()返回的结果相同
public string getLocalizedMessage();
4. 在控制台上打印Throwable对象封装的异常信息
public void printStackTrace();
异常中的两个重要方法
String getMessage() | 返回异常的详细消息字符串 |
---|---|
void printStackTrace() | 追踪堆栈异常信息(采用异步线程) |
处理异常
对异常捕获:try…catch:积极处理
try中包含可能发生异常的语句,catch中编写针对捕获的异常进行处理的代码
catch后面的小括号中的类型可以是 具体的异常类型,也可以是该异常类型的 父类型。
catch可以写多个。建议catch的时候,精确的一个一个处理。这样有利于程序的调试。
catch写多个的时候,从上到下,必须遵守 从小到大。
异常抛出:throws:消极处理
支持一次性抛出多种异常
在方法声明的位置上使用 throws 关键字抛出,谁调用我这个方法,我就抛给谁。抛给 调用者(谁调用谁管,调用者可以积极处理也可以消极处理) 来处理。
throw关键字
throw用于方法体内,throws用于方法的声明中,throw只能抛出一个异常类对象,throws多个P146
finally子句
try语句中的代码出现了异常执行finally语句,保证程序的正常运行
通常在finally语句块中完成 资源的释放/关闭。
可以try和finally连用,没有catch
自定义异常
特殊的异常情况需要自己定义一个异常去处理,但是必须继承Exception或其子类
步骤
第一步:编写一个类继承 Exception 或者 RuntimeException.
第二步:提供两个 构造方法,一个无参数的,一个带有String参数的。
//栈操作异常:自定义异常!
public class StackOperationException extends Exception{ // 编译时异常!
public MyStackOperationException(){
}
public MyStackOperationException(String s){
super(s);
}
}
垃圾回收(java GC)
java虚拟机会自动回收垃圾对象所占用的空间
java中的常用类
String类
String声明为final的,不能被继承,定义的字符串是一个常量,一旦定义,内容和长度是不可改变的
【解释】 String类是final类故不可以继承,也就意味着String引用的字符串内容是不能被修改。String有两种实例化方式:
(1)直接赋值(例中,String str = “Hello”;就是直接赋值实例化了)
(2)使用new调用构造方法完成实例化;
要注意的是:String类对象内容不能修改,但并不代表其引用不能改变,下面通过内存的分配图说明字符串不可改变的真正含义:
可知,String对象内容的改变实际上是通过内存地址“断开-连接”变化来完成的,而原字符串中的内容并没有任何的改变。String str = “Hello”;和str = str + " World";实质上是开辟了三个内存空间,str只是由原来指向"hello"变为指向“hello world”而已,而其原来的指向内容,是没有改变的。
因此,在以后的开发中,若要经常修改字符串的内容,请尽量少用String,因为字符串的指向“断开-连接”会大大降低性能;对于要经常修改内容的情况,建议使用:StringBuilder、StringBuffer
String实现了Serializable接口:表示字符串是支持序列化的
实现了Comparable接口:表示String可以比较大小
String:代表不可变的字符序列。简称:不可变性。(只要对字符串内容进行任何修改,都必须重新造!)
体现:
- 当对字符串重新赋值时,需要重写指定内存区域赋值,不能使用原有的value进行赋值
- 当对现有的字符串进行连接操作时,也需要重新指定内存区域赋值,不能在原有的value上进行赋值
- 当调用String的replace()修改指定字符或字符串时,也需要重新指定内存区域赋值
通过字面量的方式(区别于new)给一个字符串赋值,此时的字符串值声明在字符串常量池中
字符串常量池中是不会存储相同内容的字符串的。
String类的定义
- 初始化String对象:
String 变量名=字符串
String str=null
初始化为空
String str=""
初始化为空字符串
String str="abc"
初始化为abc,abc为字符串常量 - String的构造方法初始化字符串对象
String 变量名=new String(字符串)
String类的常见操作
字符串基本操作
index(int ch):字符第一次出现的位置
lastIndexOf(int ch):字符最后一次出现的位置
indexOf(String str):返回指定子字符串在此字符串中第一次出现处的索引
lastIndexOf(String str):指定子字符串最后一次出现的位置
charAt(int index):字符串index位置上的字符
P156
字符串的转换
char[] toCharArray();:字符串转换为一个字符数组
toLowerCase:转换为小写
toUpperCase:转换为大写
等等P156
StringBuffer类
字符串缓冲区:对字符串进行修改,内容和长度可以改变,
StringBuffer类似于一个字符容器,在其中添加或删除字符时,所操作的都是这个字符串,并不会产生新的StringBuffer对象
定义
必须是构造器生成
StringBuffer 变量名=new StringBuffer();
StringBuffer():初始容量为16的字符串缓冲区
StringBuffer(int size):构造指定容量的字符串缓冲区
StringBuffer(String capacity):将内容初始化为指定字符串内容
常用类的方法 P162
StringBuilder
功能和方法与StringBuffer基本相同,StringBuilder没有实现线程安全功能,性能略高
System类
System类定义了一些与系统相关的属性和方法,提供的属性和方法都是静态的,直接用System调用即可
getProperties:获取当前系统的全部属性
currentTimeMillis:返回一个long类型的值,表示当前时间与1970年1月1日之间的时间差,通常该值叫时间戳
arraycopy( ,。,。,。):将一个数组中的元素快速拷贝到另一个数组
Runtime类
表示Java虚拟机运行时的状态,用于封装Java虚拟机进程
Date类
Clendar.MONTH:月份是从0开始的
方法:
Date():用来创建当前日期时间的Date对象
Date(long date) 用来创建指定时间的Date对象,其中date参数表示1970年1月1日0时0分0以来的毫秒数,即时间戳
Math类P170
是一个工具类,用于完成复杂的数学运算
import java.lang.*;
public class TestMath {
public static void main(String[] args) {
System.out.println(Math.abs(-2.0)); //绝对值
System.out.println(Math.sqrt(64.0)); //立方根
System.out.println(Math.max(56,78)); //两者之间较大的
System.out.println(Math.min(56,78)); //两者之间较小的
(Math.random())
System.out.println(Math.random()); //随机数
//大于等于0.0,小于1.0的随机数
System.out.println(Math.pow(2,10)); //幂
System.out.println(Math.ceil(18.36)); //向上取整
System.out.println(Math.floor(18.66)); //向下取整
System.out.println(Math.round(11.5)); //四舍五入
System.out.println(Math.round(-11.5)); //四舍五入
}
}
四舍五入返回int值
绝对值:2.0
立方根:8.0
两者之间较大的:78
两者之间较小的:56
随机数:0.8657276690731889
幂:1024.0
向上取整:19.0
向下取整:18.0
四舍五入:12
四舍五入:-11
Math类的round方法,它表示“四舍五入”,算法为Math.floor(x+0.5),即将原来的数字加上0.5后再向下取整,所以,Math.round(11.5)的结果为12,Math.round(-11.5)的结果为-11。
其他方法:
sin(double a) 返回角的三角正弦。
cos(double a) 返回角的三角余弦。
tan(double a) 返回角的三角正切。
log(double a) 返回 double 值的自然对数(底数是 e)。
log10(double a) 返回 double 值的底数为 10 的对数。
acos(double a) 返回一个值的反余弦;返回的角度范围在 0.0 到 pi 之间。
asin(double a) 返回一个值的反正弦;返回的角度范围在 -pi/2 到 pi/2 之间。
atan(double a) 返回一个值的反正切;返回的角度范围在 -pi/2 到 pi/2 之间。
atan2(double y, double x) 将矩形坐标 (x, y) 转换成极坐标 (r, theta),返回所得角 theta.
包装类
通过包装类将基本数据类型的值包装为引用数据类型的对象
自动装箱,基本数据类型的变量赋给对应的包装类变量,
自动拆箱,包装类对象类型赋给对应的基本数据类型变量
public static void main(String[] args) {
// TODO Auto-generated method stub
// 定义一个基本类型的变量a,并赋值为20
int a = 20;
// 自动装箱:将基本类型的变量a赋给Integer类型的变量b
Integer b = a;
System.out.println(b);
// 自动拆箱:将Integer类型的变量b赋给基本类型的变量c
int c = b;
System.out.println(c);
int i=123;
String str="456";
//通过String.valueOf()方法将基本数据类型转换为字符串
Integer integer=Integer.valueOf(i);
Integer integer2=Integer.valueOf(str);
System.out.println("将int变量转换为包装类的结果"+integer);
System.out.println("将字符串变量转换为包装类结果"+integer2);
String string=integer.toString();
String string2=integer.toString(i);
System.out.println("将包装类转换为字符串"+string);
System.out.println("返回一个指定的String对象"+string2);
int a1=integer.intValue();
System.out.println("以int类型返回该Integer的值"+a1);
int b1=Integer.parseInt("789");
System.out.println("将字符串转换为对应的基本类型的数据"+b1);
}
20
20
将int变量转换为包装类的结果123
将字符串变量转换为包装类结果456
将包装类转换为字符串123
返回一个指定的String对象123
以int类型返回该Integer的值123
将字符串转换为对应的基本类型的数据789
valueOf(String s)方法可以根据String类型的参数创建包装类对象,但是参数字符串s不能为null,而且字符串必须是可以解析为相应基本数据类型的数据
Integer integer=Integer.valueOf(“123”);
合法
Integer integer=Integer.valueOf(“12a”);
不合法
parseXxx(String s)的静态方法,将字符串转换为相应的基本类型的数据。参数s不能为null,而且同样必须是可以解析为相应基本类型的数据
Random类 P172
指定的取值范围内随机产生数字
public static void main(String[] args) {
Random random1 = new Random(10);
Random random2 = new Random(10);
for(int i=0;i<5;i++){
System.out.print(random1.nextInt(5));
}
for(int i=0;i<5;i++){
System.out.print(random2.nextInt(5));
}
}
3030130301
**10是个种子数,**如果你知道随机函数怎么编出来的话应该会明白的,random里其实是一个数列,这个数列每一位的数字接近随机分布,可以从数学上证明,但是一个数列一定是确定的,也就是第一个是什么数,第二个什么数都是固定的,就像1,2,3,4.。。。但是可以通过一个种子选取数列的起始位置,例如,上面的数列从3开始,就是3,4,。。。这就使每次的随机数都不相同。而这个种子一般使用程序运行时对应时间的秒(从某一年开始总的秒数),每次的随机数就不同了,产生可以使用的伪随机数。
即种子用于随机数生成器初始化值,随机生成器对于特定的种子值总是产生相同的随机数序列。
集合
集合概述
- 集合就像是一个容器,专门用来存储java对象(实际上是对象的引用,但习惯上称为对象),这些对象可以是任意的数据类型,并且长度可变,这些集合类都位于java.util包中
- 集合按照存储结构分为两大类,单列集合Collection,双列集合Map
1)Collection:单列集合的根接口,用于存储一系列符合某种规则的元素,有两个重要的子接口List和Set
List集合的特点是元素有序,可重复,主要实现类ArraryList和LinkedList
Set集合的特点:元素无序且不可重复,主要实现类HashSet和TreeSet(不重复但是可以有序)
2)Map:双列集合的根接口,用于存储具有键(key)和值(value)映射关系的元素
Map集合中每个元素都包含一对键值,并且Key是唯一的,在使用Map集合时,通过指定的key找到对应的value
Map主要实现类:HashMap和TreeMap
Collection接口
List和Set集合的区别
collection接口的主要方法
compareTo(Object o) 方法是java.lang.Comparable接口中的方法,
List接口
List接口继承Collection接口,List接口的对象叫做List集合,允许出现重复的元素,所有的元素是以一种线性方式进行存储的。可以通过索引访问集合中的指定元素,另外,List集合还有一个特点就是元素有序,即元素的存入顺序和取出顺序一致
public static void main(String[] args) {
// TODO Auto-generated method stub
ArrayList list=new ArrayList();
list.add("1");
list.add("2");
System.out.println(list.size());
System.out.println(list.get(1));
}
ArrayList:底层数据结构是数组,查询快,增删慢,线程不安全,效率高,可以存储重复元素
内部封装了一个长度可变的数组对象,,当存入的元素超过数组长度时,ArraryList会在内存中分配一个更大的数组来存储这些元素,理解为长度可变的数组
LinkedList 底层数据结构是链表,查询慢,增删快,线程不安全,效率高,可以存储重复元素
插入一个元素的时候,只需要修改元素之间的这种引用关系即可,删除一个节点也是
特有的方法,P196
Vector:底层数据结构是数组,查询快,增删慢,线程安全,效率低,可以存储重复元素
Collection集合遍历
Iterator遍历集合
他与两大接口不一样,两大接口主要用于存储元素,Iterator主要用于迭代访问(即遍历)Collection中的元素,因此Interator对象也被称为迭代器
迭代器遍历方式, 适用于连续内存存储方式,比如数组、 ArrayList(其实 ArrayList底层实现也是数组形式)。 缺点是只能从头开始遍历, 优点是可以边遍历边删除。
Iterator i=list.iterator();
while(i.hasNext()){//判断是否存在下一个元素
Object obj=i.next();//取出ArraryList集合中的元素
System.out.println(obj);
list.remove(obj);//出现异常
}
迭代器中调用了集合对象的remove()方法删除元素会出现异常
原因:集合中删除了元素会导致迭代器语气的迭代次数发生改变,导致迭代器的结果不准确
解决方案:
一:在调用remove方法后加一个break语句
二:不使用集合对象调用,使用迭代器对象调用remove()方法
i.remove()
foreach遍历集合
底层实现也是基于iterator,所以也只能从头开始遍历,性能上比iterator要稍慢一些,因为多了一层类型自动转换。
foreach(Object obj : list){
System.out.println(obj);//取出并打印集合中的元素
JDK1.8以后版本的forEach循环
list.forEach(obj->System.out.println(obj));
}
foreach循环的次数由容器中元素的个数决定,foreach中通过变量将当前循环的元素记住,从而将集合中的元素分别打印出来
使用foreach循环遍历集合和数组时,只能访问集合中的元素,不能对其中的元素进行修改
static String[] strs= {"a","b"};
public static void main(String[] args) {
// TODO Auto-generated method stub
for(String str : strs) {
str="c";
}
System.out.println(strs[0]+strs[1]);
for(int i=0;i<strs.length;i++) {
strs[i]="c";
}
System.out.println(strs[0]+strs[1]);
}
输出结果
ab
cc
Set接口
不像List接口一样,Set接口没有对Collection接口进行功能上的扩充,只是比Collection接口更加严格
无序,元素唯一,不能根据索引获取元素,可以存储null元素
HashSet接口
元素是不可重复的,并且都是无序的
当调用HsahSet集合的add()方法向HashSet集合中添加一个元素时,首先会调用该元素的hashCode()方法来确定元素位置。然后在调用元素对象的equals()方法来确保该位置没有重复元素,当返回结果是false时,就将元素存入集合,返回true就是有重复的元素P206
public static void main(String[] args) {
// TODO Auto-generated method stub
HashSet h=new HashSet();
h.add("1");
h.add("2");
h.add("2");//添加重复元素
h.forEach(obj->System.out.println(obj));
}
运行结果:1 2
重复的2被去掉
使用hashSet添加元素
使用add方法存入元素时,首先调用当前存入元素的hashCode方法获取对象的哈希值,然后根据哈希值计算出一个存储位置;如果该位置上没有元素,则直接将元素存入,如果该位置上有元素存在,则调用equals方法
TreeSet集合
//创建TreeSet集合
TreeSet ts=new TreeSet();
//1.向TreeSet集合中添加元素
ts.add(3);
ts.add(9);
ts.add(1);
ts.add(21);
System.out.println("创建的TreeSet集合首元素为:"+ts);
采用平衡二叉树存储数据,没有重复的元素,可以对元素排序(自动排序了?),唯一性同样需要重写hashCode和equals()方法,二叉树结构保证了元素的有序性,元素:左边的小,右边的大,中序遍历(左根右)
根据构造方法的不同分为自然排序(无参,默认情况下使用)和定制排序(有参)
集合中的元素在进行比较时,都会调用compareTo()方法,该方法是Comparable接口中定义的,
Map接口
Map接口简介
Map接口是一种双列集合,每个元素都包含键对象Key和值对象Value,键和值对象之间存在一种对应关系,称为映射。映射关系是一对一的,是任意数据类型,Key不允许重复。
HashMap 非线程安全
HashMap:基于哈希表实现。使用HashMap要求添加的键类明确定义了hashCode()和equals()[可以重写hashCode()和equals()],为了优化HashMap空间的使用,您可以调优初始容量和负载因子。
TreeMap:所有的键都是按照某种顺序排列的非线程安全基于红黑树实现。TreeMap没有调优选项,因为该树总处于平衡状态。
HashMap集合
是Map接口的一个实现类,用于存储键值映射关系该集合的键和值允许为空,但键不能重复,且集合中的元素是无序的
HashMap底层是由哈希表接口组成的,就是“数组+链表”的组合体,数组时HashMap的主体结构,链表则是为了解决哈希值冲突而存在的分支结构。
//创建HashMap对象,开头可不可以用HashMap?
Map map=new HashMap();
//1.向Map存储键值对元素
map.put("1","a");
map.put("2","b");
map.put("3","c");
map.put("4","d");
//输出
System.out.println(map);
//2.查看键对象是否存在
System.out.println(map.containsKey("1");
输出结果:
和HashSet结果方式一样
{1=a, 2=b, 3=c, 4=d}
true
Map集合的遍历
Iterator迭代器遍历Map集合
先将Map集合转换为Iterator接口对象,然后进行遍历。有两种方法进行遍历,keySet()和entrySet()方法
- keySet方法:先将Map集合中所有的键对象转换为Set单列集合,接着将包含键对象的Set集合转换为Iterator接口对象,然后遍历Map集合中的所有的键,再根据键获取相应的值
//获取键的集合
Set keySet=map.keySet();
//迭代建的集合(Iterator接口对象)
Iterator it=keySet.iterator();
while(it.hasNext()){
Object key=it.next();
//获取每个键对应的值
Object value=map.get(key);
System.out.println(key+":"+value);
}
- entrySet方法:将原有Map集合中的键值对作为一个整体返回为Set集合,接着将包含键值对对象的Set集合转换为Iterator接口对象,然后获取集合中的所有的键值对映射关系,再从映射中取出键和值
P217
forEach方法遍历Map集合
map.forEach((key,value) ->System.out.println(key+":"+value))
使用LinkedHashMap集合保证元素添加顺序
让存入和取出的顺序一致,是HashMap的子类,和LinkedList一样也是用双向链表来维护内部元素的关系,使LinkedHashMap元素迭代的顺序与存入的顺序一致。P220
TreeMap集合
也是用来存储键值映射关系的,不允许出现重复的键。通过二叉树的原理来保证键的唯一性,
public static void main(String[] args) {
// TODO Auto-generated method stub
Map map=new TreeMap();
map.put("2","aaa");
map.put("1","ccc");
map.put("3","ddd");
System.out.println(map);
}
也可以定制排序:P221
key2.compareTo(key1),最终输出结果按照键对象从大到小的顺序排序
Properties集合
主要用来存储字符串类型的键和值P222
三:java编程基础
修饰符 class 类名{
程序代码
}