包
java(java原生包)
lang包,提供的核心类库,随着程序的启动再加载,使用时不需要导包
util包,操作类和对象的工具
beans包:框架
io包:输入输出流
math:提供了简单的数学运算
net:网络传输
nio包:高并发
time包:代表时间和日期
sql包:操作sql语句
security: 安全
javax(java扩展包)
org(第三方包)
API
api application progrmmer interface(应用·程序 接口)
API中所有的方法要么是被public修饰要么是被protected
Object
clone()
// An highlighted block
package ObjectDemo;
//类实现cloneable接口产生的对象支持克隆操作
//cloneable接口中没有任何内容只是给类增加标记,标记可以支持克隆操作
public class Demo implements Cloneable{
int i=1;
public void m() {
System.out.println(i);
}
public static void main(String[] args) throws CloneNotSupportedException {
Demo demo=new Demo();
Demo clone1 = (Demo) demo.clone();
//输出克隆的属性值和源对象的属性值一致
System.out.println(clone1.i);
clone1.m();
// System.out.println(s);
System.out.println(clone1 == demo);
}
}
finalize()
通知系统进行垃圾回收
getClass()
//返回的是Object实际创建类的类型,包名 System.out.println(demo.getClass());
hashcode()
//哈希码值取值范围有41亿左右,取值范围很大,重复概率极低。
//哈希码值是散列分布,可以进行均匀分布
//根据以上两个特点可得数据重复的概率几乎是不会出现--取值是唯一的
//内存地址值是不能重复只能是唯一的----用哈希码值是表示地址值
System.out.println(new Object().hashCode());
System.out.println(Integer.toHexString(new Object().hashCode()));
equals()
重写步骤:
1.判断对象地址值是否相同
2.判断参数对象是否为null
3.判断两个对象的类型是否一致
4.判断对象的属性值是否一致
Person person=new Person();
person.age=10;
person.name=new String("丽丽");
System.out.println(person.getClass());
Person person1=new Person();
/*person1.age=10;
person1.name=new String("丽丽");*/
System.out.println(person1.getClass());
System.out.println(person.equals(person1));
class Person{
String name;
int age;
@Override
public boolean equals(Object obj) {
//第一步:先判断对象本身的地址值
//第二步:判断对象的属性值
if (this == obj){
return true;
}
if (obj==null){
return false;
}
//为了判断包以及包的类名
if (this.getClass()!=obj.getClass()){
return false;
}
//重写的功能是既比较地址值又比较内容值
Person p=(Person) obj;
if (this.age != p.age){
return false;
}
//如果this.name的地址值为null,再从null获取信息,会导致报错--nullPointerException
//this.name == p.name 为了避免两个值都为null
//
if (this.name == p.name ||this.name!=null&&this.name.equals(p.name)){
return true;
}
return false;
}
}
String
最终类,没有子类
java中所有的字符串都是String类的对象
字符串是常量,它的值不能被创建后不能改变
String类对象的内容存储在底层无法改变的字符数组中,String类创建出的对象值也就无法改变。
java中所有的常量存储在方法区的运行时常量池(字符串常量池存储的是堆内存的地址)
如果参与运算的都是常量,那么在编译时期就可以进行优化直接进行运算,把运算之后的结果来进行赋值
String底层是由不可改变的字符数组来进行存储所以对象的值不能改变---------不能做拼接
String类借由别的类来做内容拼接
new StringBuilder
如果给定的字符串常量内容与之前的出现的内容一致,公用同一个方法区常量引用---------共享
toString方法对Object里面的toString方法重写
StringBuilder和StringBuffer
- Stringbuffer:从jdk1.0出现用于实现String类的内容拼接,线程安全但是拼接效率较低
- Stringbuilder:从jdk1.5开始出现用于实现String类内容的拼接,线程不安全但是拼接效率较高。
- 当对字符串进行修改的时候,需要使用 StringBuffer 和 StringBuilder 类。StringBuffer 和 StringBuilder 类的对象能够被多次的修改,并且不产生新的未使用对象。
//线程安全
StringBuffer s=new StringBuffer("12345556");
s.append("wod");
s.replace(1,3,"wer");//取代
System.out.println(s.reverse());//反序输出
System.out.println(s.capacity());
System.out.println(s);
String里的方法
字符串调用length()返回字符串的长度
数组对象调用length属性返回数组的长度
charAt();根据指定的下标返回对应的字符
length()返回字符串的长度
toCharArray()讲字符串对象内容转成新的字符数组进行返回
new String(char[] cs,int offset,int count)将字符串的部分内容转成新的字符串对象来进行返回
方法区的运行时常量池(存储的是常量)、静态常量池存储的是(类的信息)
/*
先把两个字符串对象的内容复制给两个字符数组
对两个字符数组对应的位置的字符求差值
如果差值不为0则把当前差值当做结果返回
如果差值为0则挪动下标继求对应位置字符的差值
如果每个位置的字符差值都为0则其中一个字符遍历完。结果就是两个字符数组的长度的差值
如果我们的差值为正数是
如果差值为正数表明前面的字符串对应内容大于后面的字符串内容
如果差值为正数表明前面的字符串对应内容大于后面的字符串内容
如果差值为正数表明前面的字符串对应内容大于后面的字符串内容
*/
String str1="abc";
String str2="abcDE";
System.out.println(str1.compareTo(str2));
//忽略大小写
System.out.println(str1.compareToIgnoreCase(str2));
concat和contains,endsWith和startsWith
String str="abc";
System.out.println(str);
//将指定的内容拼接到原字符串的后面
//concat底层是通过新数组的扩容实现内容的拼接
//+和concat的区别:1.+底层是由StringBuilder来拼接,concat底层由数组扩容实现拼接
//2.+可以拼接任意的类型的数据但是concat只能拼接字符串对象
System.out.println(str.concat("def"));
//原串------原来的字符串对象内容
//在原串中连续的内容----子串
//字符串是否包含所需的数字
System.out.println(str.contains("ac"));
//判断指定的字符串对象是否是子串结尾,文件名后缀和身份证后缀
System.out.println(str.endsWith("c"));
//查看字符串开头的字符
//火车,车牌号,身份证
System.out.println(str.startsWith("b", 1));
System.out.println(str.startsWith("b"));
//验证码忽略大小写
System.out.println(str.equalsIgnoreCase("abC"));
关键字:instanceof关键字,左边填入的是对象右边填入的可以是父类,本类,子类
ASII码表------------占一个字符
GBK----------国际码,在
char a=‘a’ ---------- 底层占用一个字符
char a=‘占’---------底层占用两个字符
GB2312--------中文简体
ISO8859-1-------西欧码表,底层占用一个字符
Unicode编码-----------
getBytes()
String str="abc中";
//把字符串对象的内容转成对应字节数组(如果不指定这个码表就是系统默认平台码)
//如果指定了编码表就按指定的编码
//文字转数字----编码
byte[] bytes = str.getBytes("GBK");
//字节数组转成新的字符串对象
//数字转成文字-----解码
//编码和解码不一致
String s=new String(bytes);
String s1=new String(bytes,"GBK");
String s2=new String(bytes,0,5,"gbk");
System.out.println(s);
System.out.println(s1);
hashcode()
//Object类里hashcode方法进行重写,重写的功能是重新进行计算,计算过程之和字符串对象的内容有关系
String str1="abc";
String str2=new String("abc");
System.out.println(str1.hashCode());
System.out.println(str2.hashCode());
indexOf()
String str1="abcabc";
//返回指定内容(子串)第一次出现的下标值
//如果没有找到返回-1
System.out.println(str1.indexOf("ab"));
System.out.println(str1.indexOf("ab",1));
intern()
String str="abc";
String str2=new String("abc");
//指向堆中的引用转向到方法区中
str2=str2.intern();
System.out.println(str==str2);
//以下都是空串
String str1=" ";
String str11=new String("");
String str111=new String();
// String str1111=null;空指针异常,不指向任何区域
System.out.println(str1.isEmpty());
正则表达式matches()和split()
String str="a";
//如果匹配的内容时一段匹配的内容。正则里可以填入固定内容
//matches来做具体的匹配,在方法里填入正则内容时匹配的条件
System.out.println(str.matches("abc"));
//判断是否为数字
String str1="12345";
String str2="u";
System.out.println(str1.matches("\\d*"));
//只能匹配一个字符 \D是除了非数字字符
//可以过滤一些不恰当的话
System.out.println(str2.matches("[a-z]"));
//代表任意字符,加*可以匹配多个字符
//加*代表前面的字符可以不出现也可以出现一次,也可以不出现
System.out.println(str.matches(".*"));
//匹配字符串中含有的数字
System.out.println(str.matches(".*\\d.*"));
//替换成要指定的内容
String str3="asasdsdsadsadsad456";
//System.out.println(str3.replaceAll("\\d", "*"));
//把字符串中所有的非数字字符串替换成空,只剩数字字符
str=str.replaceAll("\\D", "");
char[] s = str.toCharArray();
Arrays.sort(s);
System.out.println(Arrays.toString(s));
//如果切割符出现在字符串的最后面直接切掉,如果在前面直接是空串
String str="123a123dsd3df432sf4dfdsf4ref32323";
//切割符堆字符串对象切割得到一个字符串数组
String[] split = str.split("\\d");
for (int i=0;i<split.length;i++){
System.out.println(split[i]);
}
replace()和substring()
String s="sdsdsd";
//取代s中的字符串s
System.out.println(s.replace("s", "a"));
//从指定下标来截取子串,不包含结尾下标
System.out.println(s.substring(2,4));
trim()
String s=" sdsdsd ";
//把字符串的前后空格的去掉
System.out.println(s.trim());
valueOf()
运用环境:
String s=" sdsds";
boolean b=true;
//valueOf把任意基本类型的数据转换为字符串类型
System.out.println(String.valueOf(b));
包装类
为什么提供?
因为八种基本类型不够用,为了提高基本数据类型的便捷性、java给每个基本数据类型提供了对应的类(包装类)、创建对应类的对象可以操作对应功能,提高操作基本数据类型的效率
调用对应的valueOf()接受基本类型的数据返回对应的包装对象
八种包装数据类型的除了boolean和char的父类是Object,其他都是Number
Number是一个抽象类,
基本类型数据直接赋值给包装类对象----封箱
public class IngeterTest {
public static void main(String[] args) {
//doSome方法需要传入一个数字进去
//但是数字属于基本数据类型,而doSome方法参数的类型都是Object
//可见doSome方法无法接受基本数据类型的数字,可以接收一个数字对应的包装类
MyInt myInt=new MyInt(100);
doSome(myInt);
}
public static void doSome(Object obj){
System.out.println(obj);
}
}
+
装箱和拆箱
package com.integer;
public class IngeterTest1 {
public static void main(String[] args) {
//基本数据类型转换为引用数据类型:装箱
Integer integer = new Integer(100);
//引用数据类型转换为基本类型:拆箱
float v = integer.floatValue();
System.out.println(v);
//自动装箱
Integer x=100;
//自动拆箱
int y=x;
}
}
Math()
//随机数,从0.0到1.0的随机数(取不到1.0)
System.out.println(Math.random());
//在10~40之间
System.out.println(10 + (int) Math.random() * 31);
//向上取整
System.out.println(Math.ceil(1.2333333333333331));
//向下取整
System.out.println(Math.floor(1.99999999999));
//四舍五入
System.out.println(Math.round(1.35));
//第一个参数当做底数
System.out.println(Math.pow(1.2, 2.0));
日历
Calendar c=Calendar.getInstance();//子类对象
c.setTime(new Date(2008-1900,8-1,8));
//获取对象中包含属性信息(静态常量)
System.out.println(c.get(Calendar.DAY_OF_MONTH));
异常
什么是异常?
一套用于发现问题、反馈问题以及解决问题的机制
Throwable是异常的顶级父类
两个子类
Error---------合理的应用程序且不应该试图抓住的一类严重问题(调整外部环境或者需要来解决这个严重的问题)
Exception----------合理的应用程序且可以解决也可以不解决
解决方式:抛出和捕获
两种异常
编译时异常:编译时期出错一定要处理,除了RuntimeException累以及子类以外都是编译时异常(包含Exception) 运行时异常:编译时期没错,运行时期有错,可以处理也可以不处理,RuntimeException类以及子类都是运行时异常
package cn.tedu.exception;
public class ExceptionDemo2 {
//如果是在main上抛出异常就是JVM来解决
//public static void main(String[] args) throws FileNotExitsException{
public static void main(String[] args) {
try {//try块里存放的是有可能出现异常的代码
String str=readFile(null);
//方法上抛出了多少个编译时异常需要通过多少个catch来捕获
} catch (FileNotExitsException e) {//catch块会时时检测try块是否出现异常
//如果try块里有异常发生catch块通过对应异常类类型来捕获对应的异常类对象
//catch (FileNotExitsException e)声明一个对象用于来接收传递的异常类对象
//=new FileNotExitsException("亲,盘符找不到");
//e来接收传递的异常类对象
//e指向异常类对象可以调用异常类方法返回问题的描述信息(返回的是私有化属性值)
System.out.println(e.getMessage());
}catch (FileNotFindException e){//=new FileNotFindException("亲,您的文件类型不对");
//子类对象调用父类方法返回父类的私有化属性值(问题描述信息)
System.out.println(e.getMessage());
}catch (NullPointerException e){
//打印栈轨迹
e.printStackTrace();
}
//异常解决之后的代码正常执行
System.out.println("读取完毕");
}
//如果方法上抛出多个异常需要通过,来分隔
private static String readFile(String path) throws FileNotExitsException,FileNotFindException,NullPointerException{
//读取文件内容
//判断参数对象是否为null值
if(path==null){
//发现问题,反馈问题
throw new NullPointerException();//运行时异常可以处理也可以不处理
}
//判断文件类型是否是txt文件
if(!path.endsWith(".txt")){//要把所有的不是txt文件的判断进来
//文件类型不对
//发现问题,反馈问题,添加问题描述信息(向上传递异常类对象)
throw new FileNotFindException("亲,您的文件类型不对");
}
//判断路径信息是否有问题
if(path.startsWith("W")){
//盘符找不到
//发现问题,需要反馈问题(往上传递异常类对象)
//添加问题的描述信息
throw new FileNotExitsException("亲,盘符找不到");
}
return "文件内容";//反馈问题之后的代码不能执行
}
}
//自定义异常类--编译时异常(如果继承编译时异常则是编译时自定义异常类,如果继承运行时异常则是运行时自定义异常类)
//普通类继承异常类就变成自定义异常类
class FileNotExitsException extends Exception{
//属性
private String message;
//有参构造来进行属性初始化(可以给私有化属性来进行赋值)
public FileNotExitsException(String message){
this.message=message;
}
//获取私有化属性的值
public String getMessage() {
return message;
}
}
class FileNotFindException extends Exception{
//
public FileNotFindException(String message){
//super语句---调用父类构造方法
//调用父类构造方法给父类的私有化属性进行赋值
super(message);
}
}
finally
无论try块执行不执行都要走
public int m(){
int n;
try {
// n=10/0;//如果都是常量则会优化直接算出结果
}catch (Exception e){
e.printStackTrace();
}
return n;
}
集合(Collection)
概述
可以存储多个元素且长度可变的容器
泛型,用于指定集合元素的类型,由于泛型的限定元素类型只能是引用数据类型
Collection c;-------因为有自动封箱可以存储int类型的数据
Collection c;数据类型是引用类型,元素类型是String类型
Collection是集合的顶级接口
子接口:list,set,Queue
list(列表)
ArrayList
底层默认是创建十个数组
底层是根据右移来进行扩容。扩容之后的新长度为原来长度加上原来长度的一半
增删效率较低
有序
重复
通过下标来操作元素
import java.io.FileNotFoundException;
import java.io.Serializable;
import java.util.*;
public class list{
public static void main(String[] args) throws FileNotFoundException {
List<String> list=new ArrayList<>();//接口是支持向上造型,声明类是List,创建类是ArrayList
//添加
list.add("1");
list.add("3");
list.add("9");
list.add("7");
list.add("5");
System.out.println(list.isEmpty());
//删除元素,根据下标从0开始
//最大支持的下标---------元素个数-1
//下标受元素个数限制
//删除
System.out.println(list.remove(1));
System.out.println(list.remove("1"));//如果指定的内容没有在集合中出现就不删除(不做任何操作)
list.set(1,"2");//替换
list.add(2,"2");
System.out.println(list.contains("1"));
// list.clear();
//查找
System.out.println(list.get(1));
//下标
System.out.println(list.indexOf("2"));//返回指定的内容出现的下标值,如果没有出现就会返回-1
// list.add(5,"2");
System.out.println(list);// System.out.println(list.toString());
System.out.println(list.size());
System.out.println(list.subList(1, 3));
// Object[] s = list.toArray();
String[] s1 = list.toArray(new String[list.size()]);
System.out.println(Arrays.toString(s1));
String s="weweeweweeeewewe";
System.out.println(s.length());
}
}
LinkedList
底层通过节点(静态内部类对象)来实现数据存储
通过节点之间的地址值的指向来维系节点
底层内存不连续不需要扩容。
查询效率较低,增删效率较低。
思考:如果某个场景下,增删操作和查询操作的频率相当,那么选择ArrayList还是LinkedList?
LinkedList,因为LinkedList最终占用的总内存会比ArrayList大,但是ArrayList必须开辟一个大一点的空间(不容易开辟需要进行垃圾回收来腾出连续空间),删除数据时底层容易产生回收的垃圾碎片,消耗支援,中和来说选择LinkedList效率较高。
泛型
参数化类型,jdk1.5才出现
泛型可以指定为任意引用类型,当指定为具体的类型的后续只能操作对应类型的对象—泛型的擦除(编译时期)
泛型不支持元素类型向上造型
不允许同时出现上下限
import java.util.ArrayList;
import java.util.List;
public class test {
public static void main(String[] args) {
//创建没有范围的集合对象
List list=new ArrayList();
List<Integer> list1=new ArrayList<>();
list.add("1223");
list.add(12);
//指定泛型后续只能操作对应类型的对象---------泛型查出(发生在编译时期)
list1.add(12);
//遍历集合(因为没有指定元素类型,此时每个元素类型都是Object类型需要把每个元素对象的类型进行墙砖成对应的类型
m(list);
m(list1);
}
//? extends Number代表可以接受的类型---可以接收Number类以及Number的子类
//? extends 类/接口-----可以接收类/接口以及子类/子接口
//上线
public static void m(List<? extends Number> list){
for (Object i:list){
if (i instanceof String){
String s=(String) i;
}else if (i instanceof Integer){
Integer r=(Integer)i;
}
}
}
//? extends 类/接口-----可以接收类/接口以及父类以及父接口
//不允许同时出现
//下线
public static void n(List<? super String> list) {
}
}
映射(map)
- 可以存储多个具有一对具有关系的数据容器·
K代表的是键的类型,V代表的是键的值的范围 - 键可以得到对应的值,键不能重复但是值可以重复,一个映射容器可以由多个键和多个值来组成
- 键和值可以一起操作称之为键值对,java提供一个Entry类代表键值对,创建这个类的对象就是得到一个具体的键值对,一个映射容器可以由多个键值对的对象来组成
- Map中采用Entry内部类来表示一个映射项,映射项包含Key和Value (我们总说键值对键值对, 每一个键值对也就是一个Entry)
EntrySet是实现了Set接口,
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
public class MapDemo {
public static void main(String[] args) {
Map<String,Integer> map=new HashMap<>();
map.put("abc",123);
map.put("abc1",1234);
System.out.println(map.size());
//把映射中所有的值存放在Collection中
System.out.println(map.values());
//删除map所有的键值对
// map.clear();
//底层是根据键来确定存储位置
//因为键是无序的所以键值对所在位置是无序的,并且不能保证键值对的顺序是恒久不变
//如果键重复,但是值没有重复,会把新的键对象舍弃掉把值保留下来并进行覆盖
//把映射中的=所有的键值对存放到set集合中
//Map.Entry<String, Integer>代表键值对的类型
Set<Map.Entry<String, Integer>> entries = map.entrySet();
System.out.println(entries);
System.out.println(map.get("abc"));
System.out.println(map);
//获取所有的键存放到Set集合中
//遍历映射中所有的值
// 第一种
Set<String> se = map.keySet();
for (String i: se) {
System.out.println(map.get(i));
}
//第二种方法
Set<Map.Entry<String, Integer>> entries = map.entrySet();
for (Map.Entry<String,Integer> map1:entries){
System.out.println(map1.getKey()+":"+map1.getValue());
}
}
}
HashMap
- Map是映射顶级接口
- 底层是根据数组加链表来存储数据
- 通过键来确定键值对的存储位置
- 键是无序的并且不能存储重复的键(如果遇到重复的键就会把新的键对象舍弃掉)
- 键值对无序的,不能存储重复的键值对
- 默认的初始容量是16.默认加载因子为0.75,默认扩容是在原容量的基础上增加一倍
- 允许存储null键和null值
- 可以指定默认的初始容量,指定默认初始容量时但是在底层的真实容量值是在2的n-n+1次
例如:真实容量45-------32-64
Hashtable
- Hashtable是最早的映射类
- 不允许储存null键和null值
- 默认的初始量为11,默认的加载因子为0.75
- 指定初始容量为多少底层真实的容量就为多少
- 同步试线程安全得映射
file
//创建代表文件类的对象
//创建对象时只是把路径信息放到对象身上并不检测路径信息是否存在
//等待对象做具体操作时再检测路径是否存在
File file=new File("D:\\a\\a.txt");
//创建文件
boolean newFile = file.createNewFile();
//创建文件夹,可以创建多级文件夹mkdirs
// boolean mkdir = file.mkdir();
// System.out.println(mkdir);
// System.out.println(newFile);
//文件无论是否有内容都可以删除
//不进回收站,永久删除
// System.out.println(file.delete());
//文件夹有内容删除不了
//文件中是否删除
File file=new File("D:\\");
//获取名称中含有数字的信息
//按照过滤的条件过滤指定信息,把过滤之后的信息当做成File的对象存放到数组中并进行返回
File[] files = file.listFiles(new FileFilter() {
//指定过滤规则
@Override
public boolean accept(File pathname) {
return pathname.getName().matches(".*\\d.*");
}
});
for (File f:files){
System.out.println(f);
}
//文件中是否删除
File file=new File("D:\\");
//获取名称中含有数字的信息
//按照过滤的条件过滤指定信息,把过滤之后的信息当做成File的对象存放到数组中并进行返回
File[] files = file.listFiles(new FilenameFilter() {
@Override
public boolean accept(File dir, String name) {
return name.matches(".*\\d.*");
}
});
for (File f:files){
System.out.println(f);
}
listFiles()会把当前文件夹下所有的信息当做成file对象存储在数组中并进行返回
//文件中是否删除
File file=new File("D:\\a.txt");
// file.createNewFile();
//D:\$RECYCLE.BIN隐藏文件
//会把当前文件夹下所有的信息当做成file对象存储在数组中并进行返回
File[] files = file.listFiles();
/*for (File f:files){
System.out.println(f);
}*/
//文件名和后缀名
System.out.println(file.getName());
//返回上级文件夹的路径信息
System.out.println(file.getParent());
//返回全路径信息
System.out.println(file.getPath());
//是否为隐藏文件
System.out.println(file.isHidden());
file.setLastModified(1234343454);
//返回上次修改时间,00:00:00 GMT, January 1, 1970到此时返回值
System.out.println(file.lastModified());
System.out.println(file.isDirectory());
System.out.println(file.isFile());
System.out.println(file.getFreeSpace());
boolean b = file.canRead();
System.out.println(b);
IO流
一、四大家族的首领都是抽象类
InputStream
OutputStream
Reader
Writer
1.IO流一套用于数据传输的机制
- 根据传输的方向(根据内存当作参照物)
- 传入流–从外部往内存来传入数据
- 输出流:从内存往外部传出数据
- windows中的’s’占1个字节,'中’占两个字节
- 以reader结尾的都是字符流:例如InputStreamReader是字符流
- 所有的流都是实现了Closeable接口所以都有close方法
- 所有的输出流都是实现了Flush接口,记得flush(),如果不flush,可能会导致丢失数据
== 注意:==
字节流是万能的,字符流只能读取普通文本。
2.IO流的异常捕获
往一个文件里面传入数据叫做输出流
往一个文件传出数据叫做输入流
1.在try块外声明流对象并赋值为null值,真正的初始化在try块里
2. 保证流对象只有初始化成功才能进行正常的关流
3. 无论关流成功与否都要把流对象置为null值等待系统进行垃圾回收
4. 关流中包含自动冲刷,如果关流失败发生在自动冲刷之前就有可能导致数据滞留在缓冲区,需要手动冲刷
根据数据的表现形式:字节流和字符流
形成了四大基本流(对应的四个类都是抽象类)
输出流 | 输入流 | |
---|---|---|
字节流 | OutputStream | InputStream |
字符流 | Writer | Reader |
传输场景(数据存放/获取的位置):硬盘,内存,网络、外设
3.掌握的流有16个
- 文件专属流
FileInputStream
FileOutputStream
FileReader
FileWriter- 转换流:将字节流转换为字符流
InputStreamReader
OutputStreamReader---------------------可以转换码表- 缓冲流专属流
BufferedReader
BufferedWriter
文件专属流
FileWriter()
//创建文件字符输出流对象
//在创建对象过程中会检测路径信息是否存在,如果不存在则创建
//如果路径信息已经存在且我们对应的文件有内容还是创建一个空间进行覆盖为了避免之前文件内容对之后的文件内容进行干扰
FileWriter w=new FileWriter("D:\\b.txt");
w.write("12");
w.write("上去好");
//底层把数据存放到缓存区来进行传输数据
//为了保证传输的效率规定缓冲区只有存满才传输
//此时给定的数据没有存满缓冲区所以数据滞留在缓冲区没有传输过去
// w.flush();//冲刷缓冲区,无论缓冲区是否存满都可以进行数据传输
w.close();//关闭传输通道,自带冲刷功能
//对象没用,将对象处置为null
w=null;//让对象变为无用对象等待GC回收
FileWriter w = null;
try {
//创建文件字符输出流对象
//在创建对象过程中会检测路径信息是否存在,如果不存在则创建
//如果路径信息已经存在且我们对应的文件有内容还是创建一个空间进行覆盖为了避免之前文件内容对之后的文件内容进行干扰
w=new FileWriter("D:\\b.txt");
w.write("12");
w.write("上去好");
w.append("qweer");
//避免异常出现在close之前
w.flush();
}catch (IOException e){
e.getMessage();
}finally {
if (w != null){
//底层把数据存放到缓存区来进行传输数据
//为了保证传输的效率规定缓冲区只有存满才传输
//此时给定的数据没有存满缓冲区所以数据滞留在缓冲区没有传输过去
// w.flush();//冲刷缓冲区,无论缓冲区是否存满都可以进行数据传输,一般情况下不会出现这种情况
try {
w.close();//关闭传输通道,自带冲刷功能
} catch (IOException e) {
e.printStackTrace();
}finally {
//无论close是否执行成功都需要流对象置为null
//对象没用,将对象处置为null
w=null;//让对象变为无用对象等待GC回收
}
}
}
FileReader
FileReader reader=new FileReader("D:\\1.txt");
//定义数组来代表缓冲区
char[] cs = new char[10];
//把读取到的字符放到数组中,读取结束的条件是返回-1
//返回值就是读取到字符个数
int len=0;
//reader.read(cs)读取到的字节数量,并且将读取到元素放入数组中
while ((len=reader.read(cs))!=-1){
//可以把每次读取到的字符串输出
System.out.println(new String(cs,0,len));//转换成新的字符串
}
reader.close();
IO
模式:遇到统一问题给出的解决方式
设计模式:在软件开发过程中遇到的统一问题给出的解决方案
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.FileWriter;
import java.io.IOException;
public class IoDemo {
public static void main(String[] args) {
FileReader reader = null;
FileWriter writer = null;
try {
reader = new FileReader("D:\\1.txt");
writer = new FileWriter("D:\\a.txt");
//通过字符流来实现文件复制(异常捕获)
char[] c = new char[1024 * 1024 * 10];
int len = 0;
while ((len = reader.read(c)) != -1) {
writer.write(c, 0, len);
}
//为了保证数据不会滞留在缓冲区
writer.flush();
} catch (IOException e) {
e.printStackTrace();
} finally {
if (writer != null) {
try {
writer.close();
} catch (IOException e) {
e.printStackTrace();
} finally {
writer = null;
}
}
if (reader != null) {
try {
reader.close();
} catch (IOException e) {
e.printStackTrace();
} finally {
reader = null;
}
}
}
}
}
FileOutputStream
//底层没有缓冲区
FileOutputStream fs=new FileOutputStream("D:\\4.txt",true);
fs.write("你好".getBytes());
fs.close();
FileInputStream
//底层没有缓冲区
FileInputStream fs = new FileInputStream("D:\\4.txt");
//read返回·的是一个字节编码值
int len=-1;
byte[] bytes = new byte[10];
if ((len=fs.read())!=-1){
System.out.println(new String(bytes, 0, len));
}
fs.close();
缓冲流
BufferedReader()
给其他流来提供缓存区
作用:
- 可以指定缓冲区大小,也可以使用默认大小。默认是足够大的用于大多数目的。
- 将缓冲输入从指定的文件。无缓冲,每次调用read()或readline()可能导致数据被从文件读取,转换成文字,然后返回,这可以是非常低效的。
//底层数据还是被文件字节输入流对象来做数据传输,缓冲对象只是提供缓冲区来提供传输效率
//装饰者设计模式(同类对象给本类对象增强了功能)
BufferedReader reader = new BufferedReader(new FileReader("D:\\a.txt"));
//reader.readLine();//读取一行内容
//读取结束的条件是返回null值
String s=null;
while ((s=reader.readLine())!=null){
System.out.println(s);
}
BufferedWriter()
//创建缓冲流
//底层还是根据文件字符输出流来写数据,缓冲流只是来提供传输效率的
//缓冲流是给文件字符输出流提供一个更大的缓冲流
BufferedWriter writer = new BufferedWriter(new FileWriter("D:b.txt"));
writer.write("123");
writer.newLine();//换行
writer.close();
转换流
InputStreamReader
//创转换流对象
//底层是根据文件输入流来传输数据
//首先根据文件字节流来读取数据,再根据自动提供字符类型的展示字符类型的数据
//转换流是吧字节流转成字符流
InputStreamReader reader = new InputStreamReader(new FileInputStream("D:\\1.txt"));
//读取数据
int len=-1;
char[] c = new char[10];
if ((len =reader.read())!=-1){
System.out.println(new String(c,0,len));
}
reader.close();
OutputStreamWriter
//创建转换流对象
//底层真正做数据传输的是文件字节输出流
//首先手动提供字符流来存放字符形式的数据在根据底层文件字节流来做数据传输
//转换流----------字符流转换成字节流
OutputStreamWriter writer = new OutputStreamWriter(new FileOutputStream("D:\\1.txt"));
//首先要准备数据----字符形式的数据----只能自动提供字符流来存放字符形式的数据
writer.write("上午好");
writer.close();
PrintStream
标准输出流:可以用来记录日志
//java.io.printStream,标准的字节输出流,输出到控制台上
public class PrintStreamtest {
public static void main(String[] args) throws FileNotFoundException {
//输出到控制台上
System.out.println("hello");
PrintStream ps=System.out;
ps.println("hello");
//输出流不需要手动关闭
//可以改变输出流的方向吗,
//System的方法和属性
/* System.gc();
System.exit(0);
System.currentTimeMillis();
PrintStream ps1=System.out;
System.arraycopy();*/
//标准输出流不再指向控制台而是指向log文件
PrintStream ps1=new PrintStream(new FileOutputStream("log"));
//修改输出方向,输出到log
System.setOut(ps1);//反向输出可以用来记录日志
//输出到log文件夹
System.out.println("Hello World");
}
}
public class logger {
public static void log(String mgs){
//制定一个日志文件
PrintStream out= null;
try {
out = new PrintStream(new FileOutputStream("log"));
//改变输出的方向
System.setOut(out);
//日志当前时间
Date date=new Date();
SimpleDateFormat simpleDateFormat=new SimpleDateFormat("yyyy-MM-dd");
String str=simpleDateFormat.format(date);
System.out.println(str+":"+mgs);
} catch (FileNotFoundException e) {
e.printStackTrace();
}
}
}
序列化与反序列化
序列化:把对象以及相关的信息转成字节数组
持久化:把字节数组内容最终存储在硬盘当中
反序列化:把字节数组转成对应的对象
- 可以把属性序列化过去但是方法不会被序列化
- 被static或者被transient修饰的属性不能被序列化
- 类实现Serializable接口产生的对象才支持序列化
- SerializableUID----序列化版本号
在序列化之前会根据当前类的属性和方法计算出一个版本号,这个版本号会随着对象信息一起序列化过去,在反序列之前再次根据当前类的属性和方法计算出一个版本号,如果相同则反序列化成功。
5.集合和映射的对象度不能不被序列化,只依次遍历集合和映射
ObjectOutputStream
package io;
import java.io.*;
public class ObjectOutputStreamDemo{
public static void main(String[] args) throws IOException {
//创建,如果不指定盘符信息就会将对象存储在当前工程里面
ObjectOutputStream stream = new ObjectOutputStream(new FileOutputStream("p.data"));
Person p=new Person();
p.setAge(11);
//序列化,将对象转成字节信息
stream.writeObject(p);
stream.close();
}
}
class Person implements Serializable{
private int age;
//被transient修饰的属性不能被序列化
private transient double weight;
public int getAge() {
return age;
}
public void setAge(int age){
this.age=age;
}
}
ObjectInputStream
ObjectInputStream in = new ObjectInputStream(new FileInputStream("p.data"));
//反序列化
Person p =(Person) in.readObject();
in.close();
System.out.println("age:"+p.getAge());
Properties
特点:
- 父类是Hashtable,支持序列化
- 持久的特性,
- 映射类键和值的类型都是String类型
- 文件后缀名必须是properties
public class PropertiesDemo {
public static void main(String[] args) throws IOException {
//创建对象
Properties p=new Properties();
//设置属性,map类型的
p.setProperty("name","ll");
//序列化(支持持久化)
p.store(new FileOutputStream("Person.properties"),"this is a boy");
}
}
//创建对象
Properties p=new Properties();
//加载文件信息
p.load(new FileInputStream("Person.properties"));
//展示信息
System.out.println(p.getProperty("name"));
p.list(System.out);//列举实体类所有信息
单元测试
步骤:
- 导入测试库
//要求我们的测试方法没有返回值
//没有调用者所以无法传参数
//不能是static(三无方法)
@Test
public void m(){
System.out.println(1);
}
断言
如果预测失败就会报错,但是需要开启
System.out.println("请输入小于10的数!!!!");
int num=new Scanner(System.in).nextInt();
num>>=1;
//对结果进行预测,如果预测成功则正常通过如果不成功则报错
//需要开启断言才能工作,在vm里输入-ea
assert num<5;
System.out.println(num);
线程
进程
进程:计算机上应用拆分成多小任务(操作系统可以分配最小的单位)
线程:进程可以拆分成多个小任务(CPU可以处理的最小单位)
特点:
- 在某个时刻的CPU的某个核中只能执行一个进行,这个进程只能执行一个线程,我们的CPU只能执行一个线程
- 继承Thead,重写run方法
多线程的好处:线程的工作职责要么和CPU进行交互要么和硬件交互,当线程和硬件进行交互式CPU处于空间状态,利用率较低,引入多线程去提高CPU的利用率。
抢占CPU:
线程之间存在相互抢占CPU执行权,发生在代码的每一步,导致程序出现错乱数据(重复、跳过、负数)----多线程并发安全问题
线程的两种方法
第一种
package ThreadDemo;
public class TheadDemo {
//main方法的内容会放到底层主线程来执行
public static void main(String[] args) {
TDemo t=new TDemo();
/*Thread t1=new Thread();
t1.getName();*/
//两个线程,main方法
//不会回头执行
t.start();
//主线程执行的内容
for (int i=0;i<20;i++){
System.out.println("main:"+i);
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
class TDemo extends Thread{
//描述线程的任务信息
//重写run方法就是线程任务信息
@Override
public void run() {
for (int i=0;i<10;i++){
System.out.println("run:"+i);
//线程休眠
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
第二种方法
package ThreadDemo;
public class TheadDemo2 {
public static void main(String[] args) {
RDemo r=new RDemo();
Thread t=new Thread(r);
t.start();
//主线程执行的内容
for (int i=0;i<20;i++){
System.out.println("main:"+i);
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
class RDemo implements Runnable{
//重写run方法就是线程任务信息
@Override
public void run() {
for (int i=0;i<10;i++){
System.out.println("run:"+i);
//线程休眠
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
售票多线程
package ThreadDemo;
import java.io.Serializable;
public class ThreadDemo3 {
public static void main(String[] args) {
Ticket t=new Ticket();
t.setCount(100);
Seller s=new Seller(t);
Seller s1=new Seller(t);
Seller s2=new Seller(t);
Seller s3=new Seller(t);
Thread t1=new Thread(s,"A");
Thread t2=new Thread(s1,"B");
Thread t3=new Thread(s2,"C");
Thread t4=new Thread(s3,"D");
t1.start();
t2.start();
t3.start();
t4.start();
}
}
class Seller implements Runnable{
Ticket t;
public Seller(Ticket t) {
this.t = t;
}
@Override
public void run() {
while (t.getCount()!=0){
t.setCount(t.getCount()-1);
System.out.println(Thread.currentThread().getName()+"售票员卖了一张票,剩余"+t.getCount()+"张");
}
}
}
class Ticket{
private int count;
public int getCount() {
return count;
}
public void setCount(int count) {
this.count = count;
}
}
多线程安选问题的解决方案(加锁)
同步:在某个时刻只能一个线程对象来访问资源
异步:在某个时刻多个线程之间可以相互抢占资源
异步不一定不安全,同步一定安全,不安全的一定是异步。
多线程同步代码块锁
synchronized(锁对象){线程核心内容}
根据锁对象来共享线程对象,被共享进来的线程对象可以保证执行代码块里的线程核心内容时每次只能出现一个线 程对象,就可以避免线程核心内容出现线程抢占的问题。
package ThreadDemo;
import java.io.Serializable;
public class ThreadDemo3 {
public static void main(String[] args) {
Ticket t = new Ticket();
t.setCount(100);
Seller s = new Seller(t);
Seller s1 = new Seller(t);
Seller s2 = new Seller(t);
Seller s3 = new Seller(t);
Thread t1 = new Thread(s, "A");
Thread t2 = new Thread(s1, "B");
Thread t3 = new Thread(s2, "C");
Thread t4 = new Thread(s3, "D");
t1.start();
t2.start();
t3.start();
t4.start();
}
}
class Seller implements Runnable {
Ticket t;
public Seller(Ticket t) {
this.t = t;
}
@Override
public void run() {
//同步代码块锁
//代码块去包住线程的核心内容
//锁对象(可以把线程对象共享进来的对象)
while (true) {
//synchronized (t)
/*
Seller.class,Math.class,String.class方法区支援可以共享所有的
*/
// synchronized (Seller.class) //可以把当前参与的所有的线程对象共享进来
synchronized (Seller.class) {//可以把当前参与的所有的线程对象共享进来
if (t.getCount()==0)
break;
t.setCount(t.getCount() - 1);
System.out.println(Thread.currentThread().getName() + "售票员卖了一张票,剩余" + t.getCount() + "张");
}
try {
Thread.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
class Ticket {
private int count;
public int getCount() {
return count;
}
public void setCount(int count) {
this.count = count;
}
}
可以把当前参与的线程对象共享进来的对象 方法区资源(可以共享所有的线程对象) this(前提是参与的线程对象共享同一个Runnable实现类对象)
package ThreadDemo;
public class ThreadDemo3 {
public static void main(String[] args) {
Ticket1 t = new Ticket1();
t.setCount(100);
Seller1 s = new Seller1(t);
//this当锁对象时,参与的对象只能有一个实现类对象
`在这里插入代码片`Thread t1 = new Thread(s, "A");
Thread t2 = new Thread(s, "B");
Thread t3 = new Thread(s, "C");
Thread t4 = new Thread(s, "D");
t1.start();
t2.start();
t3.start();
t4.start();
}
}
class Seller1 implements Runnable {
Ticket1 t;
public Seller1(Ticket1 t) {
this.t = t;
}
@Override
public void run() {
//同步代码块锁
//代码块去包住线程的核心内容
//锁对象(可以把线程对象共享进来的对象)
while (true) {
//synchronized (t)
/*
Seller.class,Math.class,String.class方法区支援可以共享所有的
*/
// synchronized (Seller.class) //可以把当前参与的所有的线程对象共享进来
synchronized (this) {//可以把当前参与的所有的线程对象共享进来
if (t.getCount()==0)
break;
t.setCount(t.getCount() - 1);
System.out.println(Thread.currentThread().getName() + "售票员卖了一张票,剩余" + t.getCount() + "张");
}
try {
Thread.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
class Ticket1 {
private int count;
public int getCount() {
return count;
}
public void setCount(int count) {
this.count = count;
}
}
同步方法锁
在方法上加上synchronized关键字来修饰方法,会默认提供锁对象来共享线程对象
共享进来的线程对象在执行方法内容时可以保证只能每次有一个线程对象来执行。
- 如果我们修饰的是非静态方法默认的锁对象是this。
- 如果修饰的是静态方法默认的锁对象是当前类.class方法区资源可以共享所有线程对象。
package ThreadDemo;
public class ThreadDemo5 {
public static void main(String[] args) {
Ticket1 t = new Ticket1();
t.setCount(100);
Seller1 s = new Seller1(t);
//this当锁对象时,参与的对象只能有一个实现类对象
Thread t1 = new Thread(s, "A");
Thread t2 = new Thread(s, "B");
Thread t3 = new Thread(s, "C");
Thread t4 = new Thread(s, "D");
t1.start();
t2.start();
t3.start();
t4.start();
}
}
class Seller2 implements Runnable {
Ticket1 t;
int a;
public Seller2(Ticket1 t) {
this.t = t;
}
@Override
public synchronized void run() {
//同步代码块锁
//代码块去包住线程的核心内容
//锁对象(可以把线程对象共享进来的对象)
while (true) {
//synchronized (t)
/*
Seller.class,Math.class,String.class方法区支援可以共享所有的
*/
// synchronized (Seller.class) //可以把当前参与的所有的线程对象共享进来
if (t.getCount()==0)
break;
t.setCount(t.getCount() - 1);
System.out.println(Thread.currentThread().getName() + "售票员卖了一张票,剩余" + t.getCount() + "张");
try {
Thread.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
class Ticket2 {
private int count;
public int getCount() {
return count;
}
public void setCount(int count) {
this.count = count;
}
}
死锁
是由锁的嵌套来产生思索检测来让其中一个线程对象先执行,资源就可以释放,就解决了死锁问题。
package ThreadDemo;
public class DeadLockDemo {
public static void main(String[] args) {
Scan s=new Scan();
Printer p=new Printer();
new Thread(new Runnable() {
@Override
public void run() {
synchronized (p){
p.paint();
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
synchronized (s){
s.scan();
}
}
}).start();
new Thread(new Runnable() {
@Override
public void run() {
synchronized (s){
s.scan();
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
synchronized (p){
p.paint();
}
}
}).start();
}
}
class Printer{
public void paint(){
System.out.println("写");
}
}
class Scan{
public void scan(){
System.out.println("读");
}
}
优先级(当做权重尽量避免死锁的发生)
优先级1-10,优先级越大的线程对象理论上抢占到资源的概率越大
如果我们优先级大于5,理论上抢占到资源的概率会大一点
package ThreadDemo;
public class PriorityDemo {
public static void main(String[] args) {
//创建线程对象
Thread t1=new Thread(new PDemo(),"A");
Thread t2=new Thread(new PDemo(),"B");
//设置优先级
t1.setPriority(2);
t2.setPriority(3);
t1.start();
t2.start();
}
}
class PDemo implements Runnable{
@Override
public void run() {
for (int i=0;i<30;i++){
System.out.println(Thread.currentThread().getName() + ":" + i);
try {
Thread.sleep(10);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
守护线程
如果我们的被守护线程结束,随之守护线程也结束
如果不是手动设置成的守护线程就是被守护线程
被守护线程可以有多个,每个被守护线程结束所有的守护线程才会结束
内存里面有最大的守护线程就是---------------GC
反射
概念:
通过字节码对象获取实例对象的属性等信息。
Class------代表类的类,创建的对象就是具体的类(一个具体的类等效于一个.class文件,对应一个字节码对象)
Method----------代表方法的类,创建的对象就是具体的方法
Field-----代表属性的类,创建对象就是具体的属性
Constructor-----代表构造方法的类,创建对象就是具体的构造方法
Package-------代表包的类,创建的对象就是具体的包
获取字节码对象
/*
1.通过数据类型.class来获取字节码对象
*/
Class<String> aClass = String.class;
//返回的是List接口的字节码对象
Class<List> listClass = List.class;
//返回数组的字节码对象
Class<char[]> aClass1 = char[].class;
Class<Integer> intClass = int.class;
Class<Object> objectClass = Object.class;
System.out.println(objectClass);
System.out.println(intClass);
System.out.println(aClass);
/*
2.对象.getClass()返回字节码对象
*/
Class<String> aClass2 = (Class<String>) "abc".getClass();
//通过字符串内容获取字节码对象
Class<String> aClass3 = (Class<String>) Class.forName("java.lang.String");
System.out.println(aClass3.getName());
获取实例对象
1.字节码对象调用newInstance无参方法默认调用无参构造来返回实例对象
2.先获取有参构造,在通过构造方法类的对象调用newInstance有参方法来给有参方法进行返回实例对象
//创建字节码对象
Class<String> aClass = String.class;
//通过字节码对象来获取实例对象
//newInstance()调用无参构造来构建实例对象
String s = aClass.newInstance();
//通过字节码来获取指定的有参构造
Constructor<String> c = aClass.getConstructor(String.class);
//通过构造方法类的对象调用newInstance有参方法来给有参构造来赋值并且返回实例对象
s = c.newInstance("abc");
System.out.println(s);
获取构造参数中因访问权限的控制而获取不了的参数
//创建字节码对象
Class<String> aClass = String.class;
//获取指定的有参构造,不受访问权限的控制
Constructor<String> c = aClass.getDeclaredConstructor(char[].class, boolean.class);
//暴力破解
c.setAccessible(true);
//通过构造方法类的对象来调用方法给有参构造来进行赋值返回实例对象
String s=c.newInstance(new char[]{'a','c'},true);
System.out.println(s);
改变属性的值
//创建字节码对象
Class<String> aClass = String.class;
//获取属性
Field f = aClass.getDeclaredField("hash");
f.setAccessible(true);
System.out.println(f);
String s="abc";
System.out.println(s.hashCode());
//改变实例对象的hash属性值
f.set(s,10);//第一个参数代表选择的实例对象,第二个代表传入的新的对象的属性值
System.out.println(f.hashCode());
//获取实例对象的私有化属性的值
System.out.println(f.get(s));
方法
//创建字节码对象
Class<String> aClass = String.class;
//获取指定的方法
Method charAt = aClass.getDeclaredMethod("charAt", int.class);
//创建一个String的实例对象
String s="sdb";
//通过Method选择具体的实例对象
Object invoke = charAt.invoke(s, 3);//第一个参数代表的实例对象,第二个代表的实参
System.out.println(invoke);
class中的方法
//创建字节码对象
Class<String> aClass = String.class;
//获取所有的实现接口
Class<String>[] interfaces = (Class<String>[]) aClass.getInterfaces();
for (Class c:interfaces){
System.out.println(c);
}
//获取字节码的包名+类名
System.out.println(aClass.getName());
//获取包名以及版本信息
System.out.println(aClass.getPackage());
//获取简单的类名
System.out.println(aClass.getSimpleName());
//返回父类
System.out.println(aClass.getSuperclass());
//是否是基本数据类型
System.out.println(aClass.isPrimitive());
缺点:
- 打破了封装原则
- 跳过泛型的类型检测
jdk1.5新特性
泛型
动态代理
- 静态导入
静态导入的方法先加载
import static java.lang.Math.*;
public class StaticImportDemo {
public static void main(String[] args) {
System.out.println(random());
System.out.println(abs(10));
}
}
- 可变参数
功能是可以接收任意多个参数值
底层根据数组来实现
只能出现在参数列表的最右边
public static void main(String[] args) {
System.out.println(sum(1.2,1, 2, 3, 4));
}
//参数类型----可变参数
//可变参数底层是数组来实现的
//可以接收任意多个参数(但是参数类型必须统一)
//可变参数只能出现在最右边
public static int sum(double v,int... x){
int sum=0;
for (int a:x){
sum+=a;
}
return sum;
}
- 枚举
将值一列举
可以定义属性和普通·方法
定义的构造方法只能私有化
可以定义抽象方法(枚举常量必须重写抽象方法)
枚举常量只能第一行
从jdk1.5开始出现switch中使用枚举
public enum Season {
spring{
public void n(){
}
},summer{
public void n(){
}
};
private int i;
//只能是私有化的方法
private Season(){}
public void m(){}
//不能调用到抽象方法
public abstract void n();
}
jdk1.8
接口中支持定义实体方法
public class LambdaDemo {
public static void main(String[] args) {
cac c=new cacImpl();
System.out.println(c.sum(1, 2));
}
}
interface cac{
//用default只能在接口中修饰实体方法
public default int sum(int x,int y){
return x+y;
}
}
lambda表达式
- 一个接口中只能有一个抽象方法才能实现
- lambda表达式重写函数式接口中的抽象方法
public class LambdaDemo {
public static void main(String[] args) {
//第一种方式
/*cac c=(int a,int b)->{
return a>b?a:b;
};*/
//第二种
//参数必须和方法里面的一致
cac c=(a,b)->b>a?a:b;
System.out.println(c.max(1,2));
}
}
//函数式接口(只能有一个抽象方法),函数式编程
@FunctionalInterface
interface cac{
int max(int a,int b);
}
流式结构
操作集合元素
提供了大量的函数式接口(可以使用lambda表达式)
List<String> list=new ArrayList<>();
list.add("JAVA");
list.add("PYTHON");
list.add("JA");
Stream<String> stream = list.stream();
stream.filter(new Predicate<String>() {
@Override
public boolean test(String s) {
return s.endsWith("A");
}
}).forEach(new Consumer<String>() {
@Override
public void accept(String s) {
System.out.println(s);
}
});
stream.filter(s->s.endsWith("A")).forEach(s -> System.out.println(s));