java基础总结:
方法:
是将具有独立功能的代码块组织成为一个整体,使其具有特殊功能的代码集。
方法必须先创建才可以使用,该过程成为方法的定义。
方法创建后并不是直接运行的,需要手动使用后才执行,该过程成为方法的调用。
方法必须先定义,在调用。
方法定义时,参数中的数据类型与变量名都不能缺少,缺少任意一个,程序将报错。
方法定义时,多个参数之间使用,分隔。
方法调用时,参数的数量与类型必须与方法定义中的设置相匹配,否则程序报错。
方法的形参和实参:
形参:方法定义中的参数,等同于变量的定义格式,类如,int num;
实参:方法调用中的参数,等同于使用变量或常量,类如,10;
带返回值方法的定义和调用:
定义:方法定义时return 后面的返回值与方法定义上的数据类型要匹配,否则程序将报错。
eg:public static 数据类型(int) 方法名(参数){
return 数据(100);
}
调用:方法的返回值通常会使用变量接收,否则,该返回值,将无意义。
方法名(参数);
isNumber(5);
数据类型 变量名 = 方法名(参数);
boolean flag = isNumber(5);
方法的注意事项:
方法不能嵌套定义;
void表示无返回值,可以省略retrun,也可以单独书写return,后面不加数据。
定义方法时,要做到两个明确:
明确返回值类型:主要明确方法操作完毕之后是否有数据返回,如果没有,写void,如果有写对应数据类型;
明确参数:主要明确参数的类型和数量。
调用方法时,void类型的方法,直接调用即可。非void类型的方法,推荐用变量接收调用。(方法返回什么类型,就用什么类型接收)
方法的重载
方法的重载指同一类中定义的多个方法之间的关系,满足下列条件的多个方法相互构成重载。
-
多个方法在同一个类中
-
多个方法具有相同的方法名
-
多个方法的参数名不相同,类型不同,或者数量不同。
方法重载的特点:
重载仅对应方法的定义,与方法的调用无关,调用方式参照标准格式。
重载仅针对同一个类中方法的名称与参数进行识别,与返回值无关,换句话说,不能通过返回值来判定两个方法是否构成重载。
类和对象
类 :是对现实生活中一类具有共同属性和行为的事物的抽象。
对象:是能够看的到,摸得着的真实存在的实体。
类的特点:类是 对象的数据类型,类是具有相同属性和行为的一组对象的集合。
对象的属性:对象具有的各种特征,每个对象的每个属性都具有特定的值。
对象的行为:对象可以执行的操作。
类和对象的关系:类是对象的抽象,对象是类的实体。
类的的组成:属性和行为。
属性:在类中通过成员变量来体现。(类中方法外的变量)
行为:在类中通过成员方法来体现。(去掉方法中static关键字)
创建对象 格式: 类名 对象名 = new 类名();
使用对象:
成员变量 格式:对象名.变量名
成员方法 格式:对象名.方法名
成员变量:类中方法外的变量
局部变量:方法中的变量
private关键字
是一个权限修饰符,可以修饰成员(成员变量和成员方法),作用是保护成员不被别的类使用,被private修饰的成员只能在本类中才能访问。
this关键字
this修饰的变量用于指代成员变量,方法的形参如果与成员变量同名,不带this修饰的变量指的是形参,而不是成员变量。
方法的形参没有与成员变量同名,不带this修饰的变量指的是成员变量。
什么时候使用this,解决局部变量隐藏成员变量。
this:代表所在类的对象引用,记住,方法被那个对象调用,this,就代表那个对象。
封装
是面向对象的三大特征之一(封装,继承,多态)
是面向对象编程语言对客观世界的模拟,客观世界里成员变量都是隐藏在对象内部的,外界是无法直接操作的。
封装的原则:
将类的某些信息,隐藏在类的内部,不允许外部程序直接访问,而是通过该类提供的方法来实现对隐藏信息的操作和访问成员变量的private,提供对象的getXxx(),setXxx()方法.
封装的好处:通过方法来控制成员变量的操作,提高了代码的安全性,把代码用方法进行封装,提高了代码的复用性。
构造方法:是一种特殊的方法,格式,修饰符 类名 (参数){},作用,主要是完成对象数据的初始化。
构造方法的注意事项:
构造方法的创建:如果没有定义构造方法,系统将给出一个默认的无参构造方法,如果定义了构造方法,系统将不在提供构造方法。
构造方法的重载:如果自定义了带参构造方法,还要使用无参构造方法,就必须写一个无参构造方法。
推荐使用方式,无论是否使用,都写无参的构造方法。
API(Application Programming Interface)
应用程序编程接口。
javaApi:指的是jdk中提供的各种功能的类。
继承
格式:public class 子类名 extends 父类名{}
继承中子类的特点:子类可以有父类的内容,子类还可以有自己特有的内容。
继承的好处和弊端:
好处:提高了代码的复用性(多个类相同的成员可以放到同一个类中),提高了代码的维护性(如果方法中的代码要修改,修改一处即可)
弊端:继承让类与类之间产生了关系,类的耦合性增强了,当父类发生变化时子类实现也不得不跟着变化,削弱了子类的独立性。
什么时候使用继承?
如果有两个类A和B,如果他们满足A是B的一种,或者B是A的一种,就说明他们存在继承关系,这个时候就考虑使用继承,否则不能滥用继承。
继承中变量的访问特点
在子类方法中访问一个变量,现在子类局部范围找,接着子类成员范围找,最后在父类成员范围找,如果都没有就报错(不考虑父亲的父亲)
super
方法的重写
子类中出现了和父类中一模一样的方法声明
方法重写的应用:当子类需要父类的功能 ,而功能主体子类有自己特有的内容时,可以重写父类中的方法,这样,即沿袭了父类的功能,又定义了子类特有的内容。
@Override 帮助我们检查重写方法声明的正确性。
方法重写注意事项
私有方法不能被重写(父类私有成员,子类是不能继承的)
子类方法访问权限不能更底(public>默认>私有)
java中继承的注意事项
java中类只支持单继承,不支持多继承。支持多层继承。
权限修饰符
状态修饰符
final(最终态)
关键字是最终的意思,可以修饰成员方法,成员变量,类
修饰方法,表示最终方法,不能被重写。
修饰变量,表示该变量是常量,不能再次赋值。
修饰局部变量
变量是基本类型,基本类型的数据值不能发生改变;
变量是引用类型,引用类型的地址值是不能改变的,地址里面的内容是可以改变的。
修饰类,表示类是最终类,不能被继承。
static(静态)
如果是静态通过类名就可以直接调用。
关键字是静态的意思,可以修饰成员方法,成员变量
修饰的特点:被类的所有对象共享。
静态成员方法,只能访问静态成员。
多态
同一个对象,在不同时刻表现出来的不同形态
多态的前提和体现:
有继承/实现关系,有方法重写,有父类引用,指向子类对象。
多态中成员访问特点
成员变量:编译看左边,执行看左边。
成员方法:编译看左边,执行看右边。
成员变量和成员方法访问不一样是因为:成员方法有重写,而成员变量没有。
多态的好处和弊端
好处:提高了程序的扩展性,具体表现:定义方法的时候,使用父类型作为参数,,将来在使用的时候,使用具体的子类来参与操作。
弊端:不能使用子类的特有功能。
多态中的转型
向下转型和向上转型
Animal a = new Cat(); 向上转型 从子到父,父类引用指向子类对象。
Cat c = (Cat) a; 向下转型 从父到子,父类引用转为子类对象。
抽象类
在java中,一个没有方法体的方法应该定义为抽象方法,而类中如果有抽象方法,该类必须定义为抽象类。
抽象类的特点
抽象类和抽象方法必须使用abstract关键字修饰。
public abstract class 类名{}
public abstract void eat();
抽象类中不一定有抽象方法,有抽象方法的类,一定是抽象类。
抽象类不能实例化,参照多态的方式,通过子类对象实例化,这叫抽象多态。
抽象类的子类,要么重写重写抽象类中的所有抽象方法,要么是抽象类。
抽象类的成员特点
成员变量:可以是变量,也可以是常量
构造方法:有构造方法,但是不能实例化,构造方法的作用是用于子类访问父类的数据的初始化。
成员方法:可以有抽象方法,限定子类必须完成某些动作,也可以有非抽象方法,提高代码复用性。
接口
接口就是一种公共的规范标准,只有符合规范标准,大家都可以通用。
java的接口更多体现在对行为的抽象。
接口的特点:
接口用关键字inteface修饰,格式: public interface 接口名{},类实现接口用implements表示 public class 类名 implements接口名{}
接口不能实例化,参照多态的方式,通过实现类对象实例化,这叫接口多态。
多态的形式:具体类多态,抽象类多态,接口多态。
多态的前提:有继承或者实现关系; 有方法重写;有(父类/接口)引用指向(子/实现)类对象。
接口的实现类:要么重写接口中的所有抽象方法, 要么是抽象类。
接口中的成员特点:
成员变量:只能是常量,默认修饰符:public static final
构造方法:接口没有构造方法,因为接口主要对行为进行抽象的,是没有具体存在的,如果一个类没有父类,默认继承Object类。
成员方法:之能是抽象方法,默认修饰符,public abstract
类和接口的关系
类和类的关系:继承关系,只能单继承,但是可以多层继承。
类和接口的关系:实现关系,可以单实现,也可以多实现,还可以在继承一个类的同时实现多个接口
接口和接口的关系:继承关系,可以单继承,也可以多继承
抽象类和接口的区别
成员区别:
抽象类 变量,常量;;有构造方法;有抽象方法,也有非抽象方法;
接口 常量,抽象方法;
设计理念区别:
抽象类: 对类的抽象,包括属性,行为。
接口: 对行为的抽象,主要是行为。
形参和返回值
类名作为形参和返回值:
方法的形参是类名,其实需要的是该类的对象
方法的返回值是类名,其实返回的是该类的对象
抽象类名作为形参和返回值:
方法的形参是抽象类名,其实需要的是该类的子类对象;
方法的返回值是抽象类名,其实返回的是该类的子类对象;
接口名作为形参和返回值:
方法的形参是接口名,其实需要的是该接口的实现类对象
方法的返回值是接口名,其实返回的是该接口的实现类对象
内部类
内部类就是在一个类中定义一个类,eg:在一个类A中定义一个类B,类B就称为内部类。
内部类的定义格式:
public class 类名{
修饰符 class 类名{}
}
内部类的访问特点:
内部类可以直接访问外部类的成员,包括私有。
外部类要访问内部类的成员,必须创建对象。
成员内部类:
按照内部类在类中定义的位置不同,可以分为如下两种形式
在类的成员位置:成员内部类
在类的局部位置:局部内部类
成员内部类,外界如何创建对象使用
格式:外部类名.内部类名 对象名 = 外部类对象.内部类对象;
例子:Outer.Inner oi = new Outer().new Inner;
一般是间接调用:通过创建外部类的对象,来调用外部类的方法,在方法里面创建内部类对象,通过内部类对象来调用内部类。
Outer o = new Outer();
o.method();
private class Inner(){
public void show(){
sout("xx");
}
}
public void method(){
Inner i = new Inner();
i.show();
}
局部内部类:
局部内部类是在方法中定义的类,所以外界是无法直接使用,需要在方法内部创建对象并使用
该类可以直接访问外部类的成员,也可以访问方法内的局部变量。
Outer o = new Outer();
o.method();
public class Outer{
public void method(){
class Inner{
public void show(){
}
}
Inner i = new Inner();
i.show();
}
}
匿名内部类
前提:存在一个类或者接口,这里的类可以是具体类,也可以是抽象类
格式
new 类名或者接口名(){
重写方法;
}
new Inter(){
public void show(){
}
}
本质:是一个继承了该类或者实现了该接口的子类匿名对象。
调用内部类:将实现类对象赋值给接口。
Inter i = new inter(){
@Override
public void show(){
sout("匿名内部类");
}
};
i.show();
匿名内部类在开发中的使用
直接创建接口操作类的对象,调用方法即可。不需要在创建类。
JumppingOperatro jo = new JumppingOperatro();
jo.method(new Jumpping(){
@Override
public void jump(){
sout("猫可以跳高了");
}
})
Math类
包含执行基本数字运算的方法
0-100,(int) (Math.random()*100 +1);
System类
Stystem包含几个有用的类字段和方法,它不能被实例化。
System.exit(0);终止当前java虚拟机,非0表示异常终止。
System.currentTimeMillis()*1.0/1000/60/60/24/365+“年”;返回现在离1970年多长时间。
Object类
Object是类层次结构的根,每个类都可以将Object作为超类。所有类都直接或者间接继承该类。
Arrays类
冒泡排序
排序:将一组数组按照固定的规则进行排列
冒泡排序:一种排序的方式,对将要进行排序的数据中相邻的数据进行两两比较,将较大的数据放在后边,依次对所有的数据进行操作,直至所有的数据按照要求完成排序。
原理:如果有n个数据进行排序,总共需要比较n-1次,每一次比较完毕,下一次的比较就会少一个数据参与。
代码实现:
public class Main {
public static void main(String[] args) {
int[] arr = {12,53,523,24,15};
for (int i = 0; i < arr.length-1; i++) {
for (int j = 0; j < arr.length-1-i; j++) {
if (arr[j]>arr[j+1]){
int temp = arr[j];
arr[j] = arr[j+1];
arr[j+1] = temp;
}
}
}
System.out.println(arrtoString(arr));
}
private static String arrtoString(int[] arr){
StringBuilder sb = new StringBuilder();
sb.append("[");
for (int i = 0; i < arr.length; i++) {
System.out.println("============>"+arr[i]);
if (i==arr.length-1){
// s = s +arr[i];
sb.append(arr[i]);
}else {
// s = s+arr[i];
// s = s+", ";
sb.append(arr[i]).append(", ");
}
}
sb.append("]");
System.out.println("------------"+sb);
String s1 = sb.toString();
return s1;
}
}
格式:
Arrays.sort(arr); 排序
System.out.println("遍历数组"+ Arrays.toString(arr)); 遍历
基本类型包装类
将基本数据类型封装成对象的好处在于可以在对象中定义更多的功能方法操作该数据。
常用的操作之一:用于基本数据类型与字符串之间的转换
Integer
包装一个对象中原始类型的int值
Integer integer = Integer.valueOf(78);
System.out.println(integer); //返回表示指定的int值的Integer实例
Integer integer = Integer.valueOf("78");
System.out.println(integer); //返回一个保存指定值的Integer对象String 注意必须是含数字的string eg:"123",如果为"abc" 报错For input string: "hh"
int和String的相互转化
public class Main {
public static void main(String[] args) {
int a = 14;
Integer inte = Integer.valueOf("78");
System.out.println("字符串78转化为integer的值==>"+inte);
System.out.println("对象的类:==》"+inte.getClass());
int i = Integer.valueOf(inte);
System.out.println("============");
System.out.println("将integer的值转为为int的值"+i);
//int 转为string;
String s = String.valueOf(i);
System.out.println("将int转化为String的值"+s);
System.out.println("对象的类:==》"+s.getClass());
//String转为int
int integer1 = Integer.valueOf(s);
System.out.println(integer1);
//Sting转化为int
int i1 = Integer.parseInt(s);
System.out.println(i1);
}
}
int转化为string
public static string valueOf(int):返回int参数的字符串表现形式。该方法是string类中的方法。
string转化为int
public static int parselnt(String s ):将字符串解析为int类型。该方法为integer类中的方法。
注意:string 必须为含数字的string 如:“123”,如果是“hh”报错,For input string: "hh"。
自动装箱和拆箱
装箱:把基本数据类型转化为对应的包装数据类类型
拆箱:把包装数据类类型转化为对应的基本数据类型
总结:在开发中,在使用包装类型的时候,如果做操作,最好先判断是否为null.
import java.util.Arrays;
public class Main {
public static void main(String[] args) {
Integer integer = Integer.valueOf(100);
//自动装箱
Integer integer1 = 100; //隐藏了自动装箱 Integer.valueOf(100);
integer1 =integer1.intValue() +200;
//自动拆箱
integer1+= 200; //隐藏了自动拆箱在装箱 integer1 = integer1.intValue() + 200;
Integer i = null;
i += 300; //报错 NullPointerException
//原因:i+=300;因为i是Integer包装类型,所以会调用i.intValue(), 拆箱,因为i为null,所以报错。
//总结:在开发中,在使用包装类型的时候,如果做操作,最好先判断是否为null.
Integer i = null;
if (i!=null){
i += 300;
}
}
}
日期类
Date
Date代表了一个特定的时间,精确到毫秒。
import java.util.Date;
public class Main {
public static void main(String[] args) {
Date date = new Date();
System.out.println(date);
long date1 = 1000*60*60;
Date date2 = new Date(date1);
System.out.println(date2);
}
}
输出结果: Thu May 05 19:04:47 CST 2022
Thu Jan 01 09:00:00 CST 1970
date类的常用方法:
SimpleDateFormat 类
SimpleDateFormat是一个具体的类,用于以区域设置敏感的方式格式化和解析日期。
import java.text.SimpleDateFormat;
import java.util.Arrays;
import java.util.Date;
public class Main {
public static void main(String[] args) {
Date date = new Date();
SimpleDateFormat sdf = new SimpleDateFormat();
String format = sdf.format(date); //默认格式:22-5-5 下午7:23
System.out.println(format);
SimpleDateFormat sdf1 = new SimpleDateFormat("yyyy年mm月dd日 HH:mm:ss");
String format1 = sdf1.format(date);
System.out.println(format1); //2022年27月05日 19:27:30
}
}
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Arrays;
import java.util.Date;
public class Main {
public static void main(String[] args) throws ParseException {
//格式化:从Date到String
Date date = new Date();
SimpleDateFormat sdf = new SimpleDateFormat();
String format = sdf.format(date); //默认格式:22-5-5 下午7:23
System.out.println(format);
SimpleDateFormat sdf1 = new SimpleDateFormat("yyyy年mm月dd日 HH:mm:ss");
String format1 = sdf1.format(date);
System.out.println(format1); //2022年27月05日 19:27:30
//从String到Date
String S = "2014-08-22 22:22:22";
SimpleDateFormat sdf2 = new SimpleDateFormat("yyyy-mm-dd HH:mm:ss");
Date parse = sdf2.parse(S);
System.out.println(parse);
//报这个错,ParseException: Unparseable date: "2014-08-22 22:22:22"
要检查要求的格式(yyyy-mm-dd HH:mm:ss)是否与传递的S(2014-08-22 22:22:22)匹配
}
}
Calendar类
Calendar为某一时刻和一组日历字段之间的转换提供了一种方法,并为操作日历字段提供了一些方法
Calendar提供了一个类方法getInstance用于获取Calendar对象,其日历字段已使用当前日期和时间初始化:Calendar rightNow = Calendar.getInstance();
import java.text.ParseException;
import java.util.Calendar;
public class Main {
public static void main(String[] args) throws ParseException {
Calendar c = Calendar.getInstance(); //多态的形式,抽象类用子类的对象。
System.out.println(c);
c.add(Calendar.YEAR,10); //10年后
c.add(Calendar.DATE,-5); //5天前
c.set(2012,8,4,13,22,56); //设置时间
int year = c.get(Calendar.YEAR);
int month = c.get(Calendar.MONDAY) + 1; //默认从零开始,月份要+1;
int day = c.get(Calendar.DATE);
int s = c.get(Calendar.HOUR); //12小时制 24小时制:HOUR_OF_DAY
System.out.println(year+"年"+month+"月"+day+"日"+s);
}
}
输出结果:java.util.GregorianCalendar[time=1651819824233,areFieldsSet=true,areAllFieldsSet=true,lenient=true,zone=sun.util.calendar.ZoneInfo[id="Asia/Shanghai",offset=28800000,dstSavings=0,useDaylight=false,transitions=19,lastRule=null],firstDayOfWeek=1,minimalDaysInFirstWeek=1,ERA=1,YEAR=2022,MONTH=4,WEEK_OF_YEAR=19,WEEK_OF_MONTH=1,DAY_OF_MONTH=6,DAY_OF_YEAR=126,DAY_OF_WEEK=6,DAY_OF_WEEK_IN_MONTH=1,AM_PM=1,HOUR=2,HOUR_OF_DAY=14,MINUTE=50,SECOND=24,MILLISECOND=233,ZONE_OFFSET=28800000,DST_OFFSET=0]
2022年5月6日
案例:获取每年的二月有多少天
import com.sun.scenario.effect.impl.sw.sse.SSEBlend_SRC_OUTPeer;
import java.text.ParseException;
import java.util.Calendar;
import java.util.Scanner;
public class Main {
public static void main(String[] args) throws ParseException {
Scanner scanner = new Scanner(System.in);
int i = scanner.nextInt();
Calendar c = Calendar.getInstance();
c.set(i,2,1);
c.add(Calendar.DATE,-1);
int i1 = c.get(Calendar.DATE);
System.out.println(i1);
}
}
异常
异常:就是程序出现了不正常的情况
jvm的默认处理方案
如果程序出现了问题,我们没有做任何处理,最终jvm会做默认的处理
把异常的名称,异常原因及异常出现的位置等信息输出在了控制台
程序停止运行
异常处理:
如果程序出现了问题,我们需要自己来处理,有两种方案:
try … catch …
throws
try … catch …
try … catch …格式:
try {
可能出现异常的代码;
} catch(异常类名 变量名) {
异常处理的代码;
}
执行流程:
程序从try里面的代码开始执行,出现异常,会自动生成一个异常类对象,该异常对象将被提交给java运行时系统。当java运行时系统接收到异常对象时,会到catch中去找匹配的异常类,找到后进行异常的处理,执行完毕之后,程序还可以继续往下执行。
try ... catch ...案例:
public class Main {
public static void main(String[] args) {
System.out.println("开始");
method();
System.out.println("结束");
}
private static void method() {
try {
int[] arr = {1,2,3};
System.out.println(arr[3]); //new ArrayIndexOutOfBoundsException();
}catch (ArrayIndexOutOfBoundsException e){
System.out.println("访问的数组越界");
e.printStackTrace();
}
}
}
Throwable的成员方法
编译时异常和运行时异常的区别
java中的异常被分为两大类:编译时异常和运行时异常,也称为受检异常和非受检异常,所有的RuntimeExceptionl类及其子类被称为运行时异常,其他的异常都是编译时异常。
编译时异常:必须显示处理,否则程序就会发生错误,无法通过编译
运行时异常:无需显示处理,也可以和编译时异常一样处理
throws
处理没有权限的异常处理。
格式:
throws 异常类名;
注意:这个格式是跟在方法的括号后面的
编译时异常必须要进行处理: 两种处理方案:try … catch … 或者throws,如果采用throws这种方案, 将来谁调用谁处理。throws没有解决问题,只是将异常抛出,让程序运 行,最后还是需要try … catch … 来解决。
运行时异常可以不处理:出现问题后需要我们回来修改代码。
自定义异常
java提供的异常类,不能满足我们所有的需求,自定义异常可以对我们需要盘的的某些异常做专门的捕获。我们都会根据对应的异常给予用户相应的提示信息。让用户知道自己是不是做错了什么操作。还有当我们捕获到对应的异常,我们能够根据这个异常做相关的后续操作。
格式:
public class 异常类名 extends Exception {
无参构造
带参构造
}
范例:
public class ScoreException extends Exception {
public ScoreException(){}
public ScoreException(String message){
super(message);
}
}
throws 和throw的区别
throws
用在方法声明后面,跟的是异常类名,
表示抛出异常,由该方法的调用者来处理,
表示出现异常的一种可能性,并不一定发生这些异常,
throw
用的方法体内,跟的是异常对象名,
表示抛出异常,由方法体内的语句处理,
执行throw一定出现了异常
ScoreException类
public class ScoreException extends Exception{
public ScoreException (){}
public ScoreException (String message){
super(message); //通过getmessage()就可以拿到异常message的消息
}
}
Main测试类
import java.util.Scanner;
public class Main {
public static void main(String[] args) {
Scanner scanner = new Scanner(System.in);
System.out.println("请输入学生分数:");
int score = scanner.nextInt();
Student student = new Student();
try {
student.checkScore(score);
} catch (ScoreException e) {
e.printStackTrace();
}
}
}
Student类
public class Student {
public void checkScore(int score) throws ScoreException{
if (score<0 || score>100){
// throw new ScoreException(); //无参
throw new ScoreException("你输入的分数有误,分数应该在0-100之间");
}else {
System.out.println("分数正常");
}
}
}
输出结果:
请输入学生分数:
112
ScoreException: 你输入的分数有误,分数应该在0-100之间
at Student.checkScore(Student.java:6)
at Main.main(Main.java:10)
集合
提供一种储存空间可变的储存类型,储存的数据容量可以发生改变。
ArrayList
可调整大小的数组实现,
集合类的特点:提供一种存储空间可变的存储模型,存储的数据模型可以随时发生改变
集合类的体系结构
Collection
Collection是单例集合的顶层接口,它表示一组对象,这些对象也被称为Collection的元素.
jdk不提供此接口的任何直接实现,它提供更具体的子接口(如Set,List)实现。
创建Collection集合对象
多态的方式
具体的实现类ArrayList
Collection的常用方法
alt +7 打开一个窗口,查看类的所有信息
Collection集合的遍历
Iterator:迭代器,集合的专用遍历方式
Iterator
迭代器是通过集合的iterator方法得到的,所以我们说它是依赖于集合而存在的。
iterator中的常用方法
E next():返回迭代中的下一个元素
boolean hasNext():如果迭代具有更多元素,则返回true
import java.util.ArrayList;
import java.util.Iterator;
import java.util.Scanner;
public class Main {
public static void main(String[] args) {
ArrayList<String> c = new ArrayList<>();
c.add("java");
c.add("python");
c.add("mysql");
Iterator<String> it = c.iterator();
System.out.println(it.next());
System.out.println(it.next());
System.out.println(it.next());
System.out.println(it.next()); //NoSuchElementException;表示请求的元素不存在
while (it.hasNext()){ //判断有没有元素
String s = it.next(); //有元素输出
System.out.println(s);
}
}
}
集合的使用步骤
List
List集合概述
有序集合(也成为序列),用户可以精确控制列表中每个元素的插入位置。用户可以通过整数索引访问元素,并搜索列表中的元素。
与Set元素不同,List列表通常允许重复的元素
List集合特点:
有序:存储和取出的元素顺序一致
可重复:存储的元素可以重复
List集合特有方法
并发修改异常
并发修改异常:ConcurrentModificationException
产生原因:迭代器遍历的过程中,通过集合对象修改了集合元素中的元素长度,造成了迭代器获取元素中判断预期修改值和实际修改值不一样。add方法,里面让modCount++;导致实际修改值增加。
解决方案:用for循环遍历,然后用集合对象做对应的操作即可。
ListIterator
ListIterator:列表迭代器
通过 List集合的listIteratro()方法得到,所以说它是List集合特有的迭代器
用于允许程序员沿任一方向遍历列表的列表迭代器,在迭代期间修改列表,并获取列表中迭代器的当前位置。
ListIterator中的常用方法
E next():返回迭代中的下一个元素
boolean hasNext():如果迭代有更多的元素,则返回true
E previous():返回列表中的上一个元素
boolean hasPrevious():如果此列表迭代器在相反方向遍历时具有更多元素,则返回true
void add(E e): 将指定的元素插入列表 重新赋值。expectedModCount = modCount;
增强for循环
增强for:简化数组和Collection集合的遍历
实现Iterable接口的类允许其对象成为增强for语句的目标
它是jdk5之后出现的,其内部原理是一个Iterator迭代器,会有并发处理异常
增强for循环的格式:
for(元素数据类型 变量名:数组或者Collection集合){
//在此处使用变量即可,该变量就是元素
}
范例:
int[] arr = {1,2,3,4,5};
for(int i :arr){
System.out.println(i);
}
如果在操作的过程中,要使用到索引,用普通for循环,否则单纯遍历使用增强for。
数据结构
数据机构是计算机储存、组织数据的方式。是指相互之间存在一种或多种特定关系的数据元素集合,通常情况下,精心选择的数据结构可以带来更高的运行或者储存效率。
栈:是一种数据先进后出的模型
队列:是一种数据先进先出的模型
数组:是一种查询快,增删慢的模型
链表:是 一种增删快,查询慢的模型(对比数组)
List集合子类的特点
List集合常用子类:ArrayList,LinkedList
ArrayList:底层数据结构是数组,查询快,增删慢
LinkedList:底层数据结构是链表,查询慢,增删快
LinkedList
Set
set集合特点
不包含重复元素的集合
没有带索引的方法,所以不能使用普通for循环遍历
哈希值
哈希值:是JDK根据对象的地址或者字符串或者数字算出来的int类型的数值
Object类中有一个方法可以获取对象的哈希值
public int hashCode():返回对象的哈希码值
对象哈希值的特点
同一个对象多次调用hashCode()方法返回的哈希值是相同的
默认情况下,不同对象的哈希值是不同的。而重写hashCode的方法,可以实现让不同对象的哈希值相同
HashSet
HashSet集合特点
底层数据结构是哈希表
对集合的迭代顺序不作任何保证,也就是说不保证存储和取出的元素顺序一致
没有带索引的方法,所以不能使用普通for循环遍历
由于是Set集合,所以是不包含重复元素的集合
HashSet集合存储元素:
要保证元素的唯一性,需要重写hashCode()和equals() 重点理解
LinkedHashSet
哈希表和链表实现的Set接口,具有可预测的迭代次序
由链表保证元素有序,也就是说元素的存储和取出顺序是一致的
由哈希表保证元素唯一,也就是说没有重复的元素
TreeSet
TreeSet集合特点
元素有序,这里的顺序不是指存储和取出的顺序,而是按照一定的规则进行排序,具体排序方式取决于构造方法
TreeSet():根据其元素的自然排序进行排序
TreeSet(Comparator comparator): 根据指定的比较器进行排序
没有带索引的方法,所以不能使用普通for循环遍历
由于是Set集合,所以不包含重复元素的集合
主函数,测试类
import java.util.ArrayList;
import java.util.Iterator;
import java.util.Scanner;
import java.util.TreeSet;
public class Main {
public static void main(String[] args) {
//创建集合对象
TreeSet<Student> st = new TreeSet<Student>();
//创建学生对象
Student s1 = new Student("王昭君", 39);
Student s2 = new Student("杨玉环", 39);
Student s3 = new Student("貂蝉", 27);
Student s4 = new Student("王昭", 39);
st.add(s1);
st.add(s2);
st.add(s3);
st.add(s4);
for (Student s : st) {
System.out.println(s.getName() + "," + s.getAge()); //ClassCastException
//类转化异常,学生类没有实现Comparable接口
}
}
}
学生类:
public class Student implements Comparable<Student>{
private String name;
private int age;
public Student() {
}
public Student(String name, int age) {
this.name = name;
this.age = 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;
}
@Override
public int compareTo(Student s) { //重写接口,记得实现接口加泛型,否则报错。
return 1; //0代表元素重复,数据不会存储进去,1升序排列,-1降序排列 (升序降序指的 是添加数据的顺序,默认前面的顺序比后面小)
//按照年龄从小到大
int num = this.age-s.age; //升序
//年龄相同,按照姓名来排序
int num2 = num==0?this.name.compareTo(s.name):num; //这里的compareTo方法是 java自带的比较姓名的方法
return num2;
}
}
自然排序Comparable
用TreeSet集合存储自定义对象,无参构造方法使用的是自然排序对元素进行排序的
自然排序就是让元素所属的类,实现Comparable接口,重写Comparable(To)方法
重写方法时,一定要注意排序规则必须要按照要求的主要条件和次要条件来写
用TreeSet集合存储自定义对象,带参构造方法使用的是比较器排序对元素进行排序的
比较器排序,就是让集合构造方法接收Comparable的实现类对象,重写comparable(To1,To2)方法
重写方法时,一定要注意排序规则必须要按照要求的主要条件和次要条件来写
比较器排序主要代码,集合接收实现类对象,实现类对象用匿名类
//创建集合对象
TreeSet<Student> st = new TreeSet<Student>(new Comparator<Student>() {
@Override
public int compare(Student s1, Student s2) {
int num = s1.getAge() - s2.getAge();
int num2 = num == 0 ? s1.getName().compareTo(s2.getName()) : num;
return num2;
}
});
import java.util.*;
public class Main {
public static void main(String[] args) {
//创建set集合对象
// HashSet<Integer> set = new HashSet<>();
TreeSet<Integer> set = new TreeSet<>(); //自动排序,默认是升序
//创建随机数
Random r= new Random();
while (set.size()<6){
int num = r.nextInt(33) + 1; //bound表示范围,100表示0-99。
set.add(num);
}
for(Integer i:set){
System.out.println(i);
}
}
}
泛型
泛型是jdk5中引入的特性,它提供了编译时类型安全检测机制,该机制允许在编译时检测检测到非法的类型,它的本质是参数化类型,也就是说操作的数据类型被指定为一个参数
就是将类型由原来的具体的类型参数化,然后在使用/调用时传入具体的类型
这种参数的类型可以用在类、方法、和接口中,分别称为泛型类,泛型方法、泛型接口。
泛型定义的格式:
<类型>:指定的一种类型的格式。这里的类型可以看成是形参
<类型1,类型2…>:指定多种类型的格式,多种类型之间用逗号隔开。这里的类型可以看成是形参。
将来具体调用的时候给定的类型可以看成是实参,并且实参的类型只能是引用数据类型
泛型的好处:把运行时期的问题提前到了编译期间,避免了强制类型转化
泛型类:
泛型类的定义格式:
格式:修饰符 class 类名 <类型> { }
范例:public class Generic
此处的T可以随便写成任意标识,常见的如T,K,E,V等形式的参数常用于表示泛型。
泛型方法:
格式:修饰符:<类型> 返回值类型 方法名 (类型 变量名) { }
范例:public
泛型接口:
格式:修饰符 interface 接口名 <类型> { }
范例:public interface Generic
类型通配符
为了表示各种泛型List的父类,可以使用类型通配符
类型通配符:<?>
List<?>:表示元素类型未知的List,它表示的元素可以匹配任何的类型
这种带通配符的List仅表示它是各种泛型List的父亲,并不能把元素添加到其中
如果说我们不希望List<?>是任何泛型List的父类,只希望它代表某一类泛型List的父亲,可以使用类型通配符的上限。
类型通配符的上限:<?extends类型>
List<? extends? Number>:它表示的类型是Number或者其子类型
除了可以指定类型通配符的上限,我们也可以指定类型通配符的下限
类型通配符的下限:<?super类型>
List<?super Number>:它表示的是number或者其父类型
可变参数
可变参数又称参数个数可变,用作方法的形参出现,那么方法参数的个数就是可变的了
格式:修饰符 返回值类型 方法名 (数据类型… 变量名) { }
范例:public static int sum (int … a) { }
import java.util.*;
public class Main {
public static void main(String[] args) {
System.out.println(sum(1,2,3));
}
public static int sum(int...a){ //a是一个数组 如果是多个参数 int b int...a
int sum = 0;
for (int i:a) {
sum += i;
}
return sum;
}
}
可变参数的主要事项
这里的变量其实是一个数组
如果一个方法有多个参数,包含可变参数,可变参数要放在最后
可变参数的使用
Arrays工具类中有一个静态方法:
public static
返回的集合不能做 增删操作,可以做修改操作
List接口中有一个静态的方法:
public static
返回的集合不能做增删改操作
Set接口中有一个静态的方法
public static
返回的集合不能做增删操作,没有修改方法
Map
Map集合概述
Interface Map<K,V> K:键的类型;V:值的类型
将键隐射到值的对象;不能包含重复的键;每个键可以映射最多一个值
创建Map集合对象
多态的方式
具体的实现类HashMap
Map集合的基本功能
Map集合的获取功能
Map集合的遍历
方式一:
1.获取所有键的集合,用KeySet()方法实现。
2.遍历键的集合,获取每一个键。用增强for实现
3.根据键去找值,用get(Object key)方法实现
import java.util.*;
public class Main {
public static void main(String[] args) {
HashMap<String, String> map = new HashMap<>();
//添加元素
map.put("1","张三");
map.put("2","李四");
map.put("3","王五");
//1.获取所有键的集合
Set<String> keySet = map.keySet();
//2.遍历键的集合,获取每一个键。用增强for实现
for (String key:keySet){
//3.根据键去找值,用get(Object,key)实现
String value = map.get(key);
System.out.println(key + "," + value);
}
}
}
方式二:
1.获取所有键值对对象的集合
Set<Map.Entry<K,V>>entrySet():获取所有键值对对象的集合
2.遍历键值对对象的集合,得到每一个键值对对象
用增强for实现,得到每一个Map.Entry
3.根据键值对对象获取键和值
用getKey()得到键
用getValue()得到值
import java.util.*;
public class Main {
public static void main(String[] args) {
HashMap<String, String> map = new HashMap<>();
//添加元素
map.put("1","张三");
map.put("2","李四");
map.put("3","王五");
//1.获取所有键值对对象的集合
Set<Map.Entry<String, String>> entrySet = map.entrySet();
//2.遍历键值对对象的集合,得到每一个键值对对象
for (Map.Entry<String, String> s:entrySet){
//3.根据键值对对象获取键和值
System.out.println(s.getKey() +","+ s.getValue());
}
}
}
HashMap集合存储学生对象,学生对象作为键,要保证键的唯一性,必须在学生类重写HashCode和equals.
ArrayList嵌套HashMap
HashMap嵌套ArrayList.
统计字符串中每个字符出现的次数
import java.util.HashMap;
import java.util.Scanner;
import java.util.Set;
public class Student{
public static void main(String[] args) {
//键盘输入字符串
Scanner sc = new Scanner(System.in);
System.out.println("请输入字符串:");
String line = sc.nextLine();
//创建HsahMap集合存放键值对
// HashMap<Character, Integer> hm = new HashMap<>(); 无序
TreeMap<Character, Integer> hm = new TreeMap<>(); TreeMap对输出的数剧进行排序
for (int i = 0; i < line.length(); i++) {
//遍历输入的字符串,作为key值
char key = line.charAt(i);
//根据key获取值value
Integer value = hm.get(key);
if (value==null){
hm.put(key,1); //第一次没有数据传入,所以值为null,传入数据,将值赋为1;
}else {
value++; //value不为空,就是已经添加了数据,然后在根据key值,就将 value+1;
hm.put(key,value); //然后将value重新赋值
}
}
//根据要求遍历输出
StringBuilder sb = new StringBuilder();
//获取所有键的集合
Set<Character> keySet = hm.keySet();
//遍历集合
for(Character key:keySet){
//根据key获取值
Integer value = hm.get(key);
sb.append(key).append("(").append(value).append(")");
}
String s = sb.toString();
System.out.println(s);
}
}
Collections
Collection:是针对集合操作的工具类
Collection类的常用方法
public static <T extends Comparable<?super T>> void sort(List
public static void reverse(List<?>list):反转指定列表中元素的顺序
public static void shuffle(List<?>list):使用默认的随机源随机排列指定的列表
经典案例,模拟斗地主
需求:通过程序实现斗地主过程中的洗牌,发牌和看牌
思路:
1.创建一个牌盒,也就是定义一个集合对象,用ArrayList集合实现
2.往牌盒里面装牌
3.洗牌,也就是把牌打散,用Collections的shuffle()方法实现
4.发牌,也就是遍历集合,给三个玩家发牌
5.看牌,也就是三个玩家分别遍历自己的牌
import java.util.*;
public class Student{
public static void main(String[] args) {
//1.创建一个牌盒,也就是定义一个集合对象,用ArrayList集合实现
ArrayList<String> array = new ArrayList<>();
//2.往牌盒里面装牌
//定义花色数组
String[] colors = {"♥","♦","♣","♠"};
//定义点数组
String[] numbers = {"2","3","4","5","6","7","8","9","10","J","Q","K","A"};
for (String color:colors){
for (String number:numbers){
//将牌与颜色拼接起来,并存到牌盒里面去
array.add(number+color);
}
}
//放入大小王
array.add("大王");
array.add("小王");
//3.洗牌,将牌打散,用Collections类的shuffle()方法实现
Collections.shuffle(array);
System.out.println(array);
//4.发牌,也就是遍历集合,给三个玩家发牌
ArrayList<String> aArray = new ArrayList<>(); //a玩家
ArrayList<String> bArray = new ArrayList<>(); //b玩家
ArrayList<String> cArray = new ArrayList<>(); //c玩家
ArrayList<String> dpArray = new ArrayList<>(); //底牌
for (int i = 0; i < array.size(); i++) {
//得到牌
String poker = array.get(i);
if (i>=array.size()-3){ //最后三张牌
dpArray.add(poker);
}else if (i%3==0){
aArray.add(poker);
}else if (i%3==1){
bArray.add(poker);
}else if (i%3==2){
cArray.add(poker);
}
}
//5.看牌,也就是三个玩家分别遍历自己的牌
lookPoker("a",aArray);
lookPoker("b",bArray);
lookPoker("c",cArray);
lookPoker("底牌",dpArray);
}
//看牌的方法
public static void lookPoker(String name,ArrayList<String> array){
System.out.print(name+"的牌是:"); //不换行
for (String poker:array){
System.out.print(poker+" ");
}
System.out.println(); //每个玩家换行
}
}
模拟斗地主升级版
需求:通过程序实现斗地主过程中的洗牌,发牌和看牌。要求:对牌进行排序
思路
1.创建HashMap,键是编号,值是牌
2.创建 ArrayList,存储编号
3.创建花色数组和点数数组
4.从0开始往HashMap里面存储编号,并存储对应的牌。同时往ArrayList里面存储编号
5.洗牌(洗的是编号),用Collections的shuffle()方法实现
6.发牌(发的也是编号,为了保证编号是排序的,创建TreeSet集合接收)
7.定义看牌方法(遍历TreeSet集合,获取编号,到HashMap集合找对应的牌)
8.调用看牌方法
import sun.reflect.generics.tree.Tree;
import java.util.*;
public class Student{
public static void main(String[] args) {
//1.创建HashMap,键是编号,值是牌
HashMap<Integer, String> hm = new HashMap<>();
// 2.创建 ArrayList,存储编号
ArrayList<Integer> array = new ArrayList<>();
//3.创建花色数组和点数数组
//定义花色数组
String[] colors = {"♥","♦","♣","♠"};
//定义点数组
String[] numbers = {"3","4","5","6","7","8","9","10","J","Q","K","A","2"};
//4.从0开始往HashMap里面存储编号,并存储对应的牌。同时往ArrayList里面存储编号
int index =0;
for (String number:numbers){ //循环的顺序不能反,否则排序会按照花色来排序
for (String color:colors){
hm.put(index,color+number);
array.add(index);
index++;
}
}
hm.put(index,"大王");
array.add(index);
index++;
hm.put(index,"小王");
array.add(index);
//5.洗牌(洗的是编号),用Collections的shuffle()方法实现
Collections.shuffle(array);
//6.发牌(发的也是编号,为了保证编号是排序的,创建TreeSet集合接收)
TreeSet<Integer> aArray = new TreeSet<>();
TreeSet<Integer> bArray = new TreeSet<>();
TreeSet<Integer> cArray = new TreeSet<>();
TreeSet<Integer> dpArray = new TreeSet<>();
for (int i =0;i<array.size();i++){
Integer poker = array.get(i);
if (i>=array.size()-3){
dpArray.add(poker);
}else if (i%3==0){
aArray.add(poker);
}else if (i%3==1){
bArray.add(poker);
}else if (i%3==2){
cArray.add(poker);
}
}
//8.调用看牌方法
lookPoker("a",aArray,hm);
lookPoker("b",bArray,hm);
lookPoker("c",cArray,hm);
lookPoker("底牌",dpArray,hm);
}
//看牌的方法
public static void lookPoker(String name,TreeSet<Integer> number,HashMap<Integer,String> hm){
System.out.print(name+"的牌是:"); //不换行
for (Integer key:number){
String poker = hm.get(key); //根据编号来获取牌
System.out.print(poker+" ");
}
System.out.println(); //每个玩家换行
}
}
File
File:它是文件和目录路径名的抽象表示
文件目录是可以通过file封装成对象的
对应File而言,其封装的并不是一个真正的存在的文件,仅仅是一个路径名而已。它可以是存在的,也可以是不存在的。将来是要通过具体的操作把这个路径的内容转换为具体存在的
file类的创建功能
import sun.reflect.generics.tree.Tree;
import java.io.File;
import java.io.IOException;
import java.util.*;
public class Student{
public static void main(String[] args) throws IOException {
//需求1:我要在G:\\itcast目录下创建一个文件java.txt
File f1= new File("G:\\itcast\\java.txt");
System.out.println(f1.createNewFile());
//需求2:我要在G:\\itcast目录下创建一个目录javase
File f2 = new File("G:\\itcast\\javase");
System.out.println(f2.mkdir());
//需求3:我要在G:\\itcast目录下创建一个多级目录java\\html
File f3 = new File("G:\\itcast\\java\\htm");
System.out.println(f3.mkdirs());
//总结:创建文件,目录和路径名称无关,和方法有关。
//createNewFile()文件/目录如果不存在,就创建文件/目录,返回true,如果存在就不创建,并 且返回false。
}
}
file类判断和获取功能
file类的删除功能
删除目录时的注意事项:
如果一个目录中有内容(目录,文件),不能直接删除。应该先删除目录中得内容,最后才能删除目录
递归
递归概述:以编程的角度看,递归指的是方法定义中,调用方法本身的现象
递归解决问题的思路:
把一个复杂的问题层层转化为一个与原问题相似的、规模小的问题求解,递归策略只需少量的程序就可描述出解题过程所需要的多次重复计算
递归解决问题要找到两个内容
1.递归出口:否则会出现内存溢出
2.递归规则:与原问题相似的规模较小的问题
递归求阶乘和
import java.util.*;
public class Main {
public static void main(String[] args) {
f(5);
System.out.println(f1(5));
}
public static int f(int n){
if (n==1){
return 1;
}else{
return n*f(n-1);
}
}
public static int f1(int n){
int sum =0;
for (int i = 1; i <=n; i++) {
sum = sum+f(i);
}
return sum;
}
}
import java.util.*;
public class Main {
public static void main(String[] args) {
Scanner sc = new Scanner(System.in);
System.out.println("请输入求阶乘的数字:");
int n = sc.nextInt();
int sum = 0;
for (int i = 1; i <= n; i++) {
int s = 1;
for (int j = 1; j <=i ; j++) {
s = s*j;
}
System.out.println(s);
sum = sum+s;
}
System.out.println(sum);
}
}
遍历目录:
需求:给定一个路径(G:\itcast),请通过递归完成遍历该目录下的所有内容,并把所有文件的绝对路径输 出在控制台。
思路:
1.根据给定的路径创建一个File对象
2.定义一个方法,用于获取给定目录下的所有内容,参数为第一步创建的File对象
3.获取给定的File目录下所有文件或者目录的File数组
4.遍历该File数组,得到每一个File对象
5.判断该File对象是否是目录
是:递归调用
不是:获取绝对输出在控制台
6.调用方法
import java.io.File;
import java.util.*;
public class Main {
public static void main(String[] args) {
//1.根据给定的路径创建一个File对象
File file = new File("E:");
//6.调用方法
getAllFilePath(file);
}
//2.定义一个方法,用于获取给定目录下的所有内容,参数为第一步创建的File对象
public static void getAllFilePath(File srcFile){
//3.获取给定的File目录下所有文件或者目录的File数组
File[] fileArray = srcFile.listFiles();
//4.遍历该File数组,得到每一个File对象
if (fileArray !=null){
for (File file:fileArray){
//5.判断该File对象是否是目录
if (file.isDirectory()){
//是:递归调用
getAllFilePath(file);
}else{
//不是:获取绝对输出在控制台
System.out.println(file.getAbsoluteFile());
}
}
}
}
}
字节流
IO流:
IO:输入\输出(Input/Ouput)
流:是一种抽象的概念,是对数据传输的总称。也就是说数据在设备间的传输称为流,流的本质是数据传输。
IO流就是用来处理设备间数据传输问题的
常见的应用 :文件复制,文件上传,文件下载。
IO流的分类:
按照数据的流向
输入流:读数据
输出流:写数据
按照数据类型来分
字节流
字节输入流,字节输出流
字符流
字符输入流,字符输出流
一般来说,我们说的IO流的分类是按照数据类型来分的
使用情况:
如果数据流通过window自带的记事本软件打开,我们还可以读懂里面的内容,就是使用字符流,
否则使用字节流。如果不知道,就用字节流。字节流万能。
字节流写数据
字节流抽象基类
InputStream:这个抽象类是表示字节输入流的所有类的超类
OutputStream:这个抽象类是表示字节输出流的所有类的超类
子类名特点:子类名称都是以其父类名作为子类名的后缀
FileOutputStream:文件输出流用于将数据写入File
FileOutputStream(String name):创建文件输出流 以指定的名称写入文件
使用字节输出流写数据的步骤:
1.创建字节输出流对象(调用系统功能创建了文件,创建字节输出流对象,让字节输出流指向文件)
2.调用字节输出流对象的写数据方法
3.释放资源(关闭此文件输出流并释放与此流相关联的任何系统资源)
字节流写数据的3种方式
字节流写数据的两个问题
字节流写数据如何换行呢?
写完数剧后,加换行符
windows: \r\n
linux: \n
mac: \r
字节流写数据如何实现追加写入呢?
public FileOutputStream(String name, boolean append)
创建文件文件输出流以指定的名称写入文件。如果第二个参数为true,则字节将写入文件的末尾而不是 开头
字节流写数据加异常处理
finally:在异常处理时提供finally快来执行所有清除操作,比如说IO流中的释放资源
特点:被finally控制的语句一定会执行,除非JVM退出
格式:
try{
可能出现异常的代码;
}catch(异常类名 变量名){
异常处理代码;
}finally{
执行所有清除操作;
}
import java.io.File;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.*;
public class Main {
public static void main(String[] args) throws IOException {
FileOutputStream fos = null;
try{
fos = new FileOutputStream("my.txt");
fos.write("hello".getBytes());
}catch (IOException e){
e.printStackTrace();
}finally {
if (fos!=null){ //保证代码的健壮性,防止出现空指针错误。
try{
fos.close();
}catch (IOException e){
e.printStackTrace();
}
}
}
}
}
字节流读数据(一次读一个字节数据)
需求:把文件fos.txt中的内容读取出来在控制台输出
FileInputStream:从文件系统中的文件获取输入字节
FileInputStream(String name):通过打开与实际文件的连接来创建一个FileInputStream,该文件由文件系统中的路径名name命名
使用字节输入流读数据的步骤:
1.创建字节输入流对象
2.调用字节输入流读数据的方法
3.释放资源
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.*;
public class Main {
public static void main(String[] args) throws IOException {
//1.创建字节输入流对象
FileInputStream fis = new FileInputStream("my.txt");
//2.调用字节输入流读数据的方法
//读一次文件
// int by = fis.read();
// System.out.println(by);
// System.out.println((char)by); //强制转化为char型
//读多次文件,如果到达文件的末尾,-1;用循环
// int by = fis.read();
// while (by !=-1){
// System.out.println((char)by);
// by = fis.read();
// }
//改进循环
/*
fis.read():读数据
by = fis.read():把读到的数据赋值给by
by !=-1:判断读到的数据是否为文件结尾-1
*/
//标准读文件的方式
int by;
while((by=fis.read())!=-1){
System.out.print((char)by); //不能加换行
}
//3.释放资源
fis.close();
}
}
字节流读数据
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.*;
public class Main {
public static void main(String[] args) throws IOException {
//1.创建字节输入流对象
FileInputStream fis = new FileInputStream("my.txt");
byte[] bys = new byte[1024]; //1024及其整数倍
int len;
while((len=fis.read(bys))!=-1){
System.out.print(new String(bys,0,len)); //不能加换行
}
//3.释放资源
fis.close();
}
}
注意导包错误:new String 是java.lang.String.
字节流复制图片
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.*;
public class Main {
public static void main(String[] args) throws IOException {
//1.创建字节输入流对象
FileInputStream fis = new FileInputStream("C:\\Users\\x大大怪\\Desktop\\imgs\\2.jpg");
FileOutputStream fos = new FileOutputStream("C:\\Users\\x大大怪\\Desktop\\imgs\\picture\\2.jpg\"");
byte[] bys = new byte[1024]; //1024及其整数倍
int len;
while((len=fis.read(bys))!=-1){
fos.write(bys,0,len); //不能加换行
}
//3.释放资源
fis.close();
fos.close();
}
}
字节缓冲流
import java.io.*;
import java.util.*;
public class Main {
public static void main(String[] args) throws IOException {
//1.字节缓冲输出流
/*
FileOutputStream fos = new FileOutputStream ("my.txt");
BufferedOutputStream bos = new BufferedOutputStream(fos);
*/
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("my.txt"));
//写数据
bos.write("niniubi\r\n".getBytes());
bos.write("kendingde\r\n".getBytes());
//释放资源
bos.close();
BufferedInputStream bis = new BufferedInputStream(new FileInputStream("my.txt"));
//一次读取一个数据
int by;
while ((by=bis.read())!=-1){
System.out.print((char)by);
}
//一次读取一个字节数组数据
byte[] bys = new byte[1024];
int len;
while((len=bis.read(bys))!=-1){
//释放资源
bis.close();
}
//3.释放资源
// bos.close();
// fos.close();
}
}
四种方式:
第一种:
第二种:
第三种:
第四种:
字符流
为什么会出现字符流
由于字节流操作中文不是特别方便,所有java就提供字符流
字符流 = 字节流 + 编码表
用字节流复制文本文件时,文本文件也会有中文,但是没有问题,原因是最终底层操作会自动进行字节拼接成中文,如何识别是中文的呢?
汉字在存储的时候,无论选择那种编码存储,第一个字节都是负数
UTF-8:3个字节
GBK: 2个字节
编码表
ASCLL字符集
GBXXX字符集
Unicode字符集
注意:默认的是UTF-8
字符串在中的编码解码问题
注意:默认的是UTF-8
字符流抽象基类
字符流写数据的五种方法
字符流读数据的两种方式

字符缓冲流
字符缓冲流特有功能
readLine只读内容,不读换行的符号,所有,要想换行,输出收到加ln :println.
最常用方法:
IO流小节
复制单级文件夹
复制多级文件夹
复制文件的异常处理
相比之下,jdk7的方式更好一些
特殊操作流
标准输入输出流
打印流
对象序列化流
Properties
多线程
实现多线程
进程
进程:是正在运行的程序
是系统进行资源分配和调用的独立单位
每一个进程都有它自己的内存空间和系统资源
线程
线程:是进程中的单个顺序控制流,是一条执行路径。
单线程:一个进程如果只有一条执行路径,则称为单线程程序。
多线程:一个进程如果有多条执行路径,则称为多线程程序。
多线程的实现方式
方式1:继承Thread类
定义一个类MyThread继承Thread类
在MyThread类中重写run()方法
创建MyThread类的对象
启动线程
两个小问题:
为什么要重写run()方法?
因为run()是用来封装被线程执行的代码
run()方法和start()方法的区别?
run():封装 线程执行的代码,直接调用,相当于普通方法的调用
start():启动线程;然后由jvm调用此线程的run()方法
设置和获取线程名称
Thread类中设置和获取线程名称的方法
void setName(String name):将此线程的名称更改为等于参数name
String getName():返回此线程的名称
通过构造方法也可以设置线程名称
如何获取main()方法所在的线程名称
public static Thread currentThread(): 返回对当前正在执行的线程对象的应用。
线程调度
线程控制
线程的生命周期
多线程的实现方式 二
定义一个类MyRunnable实现Runnable接口
在MyRunnable类中重写run()方法
创建MyRunnable类的对象
创建Thread类的对象,把MyRunnable对象作为构造方法的参数
启动线程
多线程的启动方式有两种
继承 Thread类
实现 Runnable接口
相比继承Thread类,实现Runnable接口的好处
避免了java单继承的局限性
适合多个相同程序的代码去处理同一个资源的情况,把线程和程序的代码、数据有效分离,较好的体现了面向对象的设计思想
线程同步
同步代码块
:锁多条语句操作共享数据,可以使用同步代码块实现
格式:
synchronized(任意对象){
多条语句操作共享数据的代码
}
synchronized(任意对象:就相当于给代码加锁了,任意对象就可以看成是一把锁。
同步的好处和弊端:
好处:解决了多线程数据安全问题
弊端:当线程很多时,因为每个线程都会去判断同步上的锁,这是很耗费资源的,无形中会降低程序的运行效率。
同步方法
同步方法:就是把synchronized关键字加到方法上。
格式:
修饰符 synchronized 返回值类型 方法名(方法参数){ }
同步方法锁的对象是 this.
同步静态方法:就是把synchronized关键字加到静态方法上。
格式:
修饰符 static synchronized 返回值类型 方法名(方法参数){ }
同步静态方法锁的对象是:类名.class。
线程安全的类
lock锁
一般我们使用try … finally 块来实现加锁和释放锁。
生产者和消费者
网络编程
网络编程入门
计算机网络:是指将地理位置不同的具有独立功能的多台计算机及其外部设备,通过通信线路连接起来,在网络操作系统,网络管理软件及网络通信协议的管理和协调下,实现资源共享和信息传递的计算机系统。
网络编程:在网络通信协议下,实现网络互联的不同计算机上运行的程序间可以进行数据交换。
ip地址:
InetAddress
端口
协议
协议:计算机网络中,连接和通信的规则被称为网络通信协议。
UDP协议
TCP协议
UDP通信程序
TCP通信程序
Lambda
Lambda表达式的标准格式
格式:(形式参数) -> {代码块}
形式参数:如果有多个参数,参数之间用逗号隔开;如果没有参数,留空即可
->:由英文中画线和大于符号组成,固定写法。代表指向动作。
代码块:是我们具体要做的事情,也就是以前我们写的方法体内容。
Lambda表达式的使用前提
有一个接口,接口中有且仅有一个抽象方法
无返回值无参案例:
public class Main {
public static void main(String[] args){
useEatable(() ->{
System.out.println("一天一课堂,天天没收获");
});
}
private static void useEatable(Eatable e){ Eatable e 接口名, 参数
e.eat();
}
}
无返回值有参案例:
public class Main {
public static void main(String[] args){
useEatable((String s) -> {
System.out.println(s);
System.out.println("吃苹果");
});
}
private static void useEatable(Eatable f){
f.eat("苹果");
}
}
public class Main {
public static void main(String[] args){
useEatable((int x,int y) -> {
return x+y; //具体不能看方法名,要看具体的实现代码
});
}
private static void useEatable(Eatable f){
int add = f.add(10, 20);
System.out.println(add);
}
}
Lambda表达式的省略模式
省略规则:
参数类型可以省略。但是有多个参数的情况下,不能只省略一个。
如果参数有且仅有一个,那么小括号可以省略。
如果代码块的语句只有一条,可以省略大括号和分号,甚至return。
public class Main {
public static void main(String[] args){
useEatable((int x,int y) -> {
return x+y; //具体不能看方法名,要看具体的实现代码
});
//如果参数只有一个,小括号可以省略
flyEatable(s -> {
System.out.println(s);
});
flyEatable(s-> System.out.println(s));
useEatable((x,y)-> x+y);
}
private static void flyEatable(Flyable f){
f.fly("飞机上天");
}
private static void useEatable(Eatable e){
int add = e.add(10, 20);
System.out.println(add);
}
}
Lambda表达式的注意事项
注意事项:
使用Lambda必须要接口,并且要求接口中有且仅有一个抽象方法。
必须有上下文环境,才能推导出Lambda对应的接口。
根据局部变量的赋值得知Lambda对应的接口:Runnable =() ->System.out.println(“Lambad表达式”);
根据调用方法的参数得知Lambda对应的接口:new Thread(()->System.out.println(“Lambad表达式“)).start();
Lambda表达式和匿名内部类的区别
所需类型不同
匿名内部类:可以是接口,也可以是抽象类,还可以是具体类
Lambda表达式:只能是接口
使用限制不同:
如果接口中有且仅有一个抽象方法,可以使用Lambda表达式,也可以使用匿名内部类。
如果接口中多于一个抽象方法,只能使用匿名内部类,而不能使用Lambda表达式。
实现原理不同:
匿名内部类:编译之后,产生一个单独的.class字节码文件。
Lambda表达式:编译之后,没有一个单独的.class字节码文件。对应字节码会在运行的时候动态生成。
接口的组成更新
接口的组成:
常量
public static final
抽象方法
public abstract
默认方法(java8),静态方法(java8)私有方法(java9)
接口中的默认方法
接口中默认方法的定义格式:
格式:public default 返回值类型 方法名(参数列表){ }
范例:public default void show3 (){ }
接口中默认方法的注意事项:
默认方法不是抽象方法,所以不强制被重写。但是,可以被重写,重写的时候去掉default关键字
public 可以省略,default不能省略。
接口中的静态方法
接口中静态方法的定义格式:
格式:public static 返回值类型 方法名(参数列表) { }
范例:public static void show() { }
接口中静态方法的注意事项:
静态方法只能通过接口名调用,不能通过实现类名或者对象名调用
public可以省略,static不能省略。
接口中的私有方法
方法应用
引用类方法:
格式:类名::静态方法
范例:Integer::parseInt
Integer类的方法:public static int parseInt(String s)将此String转化为int类型
引用对象的实例方法:
引用对象的实例方法,其实就是引用类中的成员方法
格式:对象::成员方法
范例:“HelloWorld”::toUpperCase
String类中的方法:public String toUpperCase()将此String所有字符 转化为 大写
引用类的实例方法
引用类的实例方法,其实就是引用类中的成员方法
格式:类名::成员方法
范例:String::substring
String类中的方法:public String substring (int beginIndex,int endIndex)
从beginIndex开始到endIndex结束,截取字符串。返回一个字串,字串的长度为endIndex-beginIndex
引用构造器
引用构造器,就是引用构造方法
格式:类名::new
范例:Student::new
函数式接口:
函数式接口:有且仅有一个抽象方法的接口
java中的函数式编程体现的就是Lambda表达式,所以函数式接口就是可以适用于Lambda使用的接口
只有确保接口中有且仅有一个抽象方法,Java中的Lambda才能顺利的进行推导。
如何检测一个接口是不是函数式接口呢?
@Functionallnterface
放在接口定义的上方:如果接口是函数式接口,编译通过,如果不是,编译失败
注意
我们自己定义函数式接口的时候,@Functionallnterface是可选的,就算我们不写这个注解,只要保证满足函数式接口定义的条件,也照样是函数式接口。但是,建议加上该注解。
函数式接口作为方法的参数
Stream流
案例:需求:按照下面的要求完成集合的创建和遍历
创建一个集合,存储多个字符串元素
把集合中所有以“王”开头的元素存储到一个新的集合
把以“王”开头的集合中,长度为3的元素存储到一个新的集合
遍历上一步得到的集合
import java.util.ArrayList;
public class Main {
public static void main(String[] args){
ArrayList<String> arrayList = new ArrayList<>();
arrayList.add("王悉知");
arrayList.add("王知");
arrayList.add("王悉");
arrayList.add("王哦知");
arrayList.add("王修知");
arrayList.add("张三丰");
arrayList.add("刘禹锡");
arrayList.add("东方不败");
ArrayList<String> wangList = new ArrayList<>();
for (String s:arrayList){
if (s.startsWith("王")){
wangList.add(s);
}
}
ArrayList<String> arraythree = new ArrayList<>();
for (String s:wangList){
if (s.length()==3){
arraythree.add(s);
}
}
for (String s:arraythree){
System.out.println(s);
}
System.out.println("------------------------------------------------");
//stream流的方式
arrayList.stream().filter(s -> s.startsWith("王"))
.filter(s -> s.length()==3).forEach(System.out::println);
}
}
stream流把真正的函数式编程风格引入到java中。
流的生成方式
stream流的使用
生成流:通过数据源(集合,数组等)生成流。list.stream()。
中间操作:一个流后面可以跟随零个或多个中间操作,其目的主要是打开流,做出某种程度的数据过滤隐射,然后返回一个新的流,交给下一个操作使用。filter()。
终结操作:一个流只能有一个终结操作,当这个操作执行后,流就被使用“光”了,无法再被操作。所以这必定是流的最后一个操作。forEach。
stream流常见的生成方式
Collection体系的集合可以使用默认方法stream()生成流
default Stream
Map体系的集合间接生成流
数组可以通过Stream接口的静态方法of(T…values)生成流。
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.stream.Stream;
public class Main {
public static void main(String[] args){
//Collection体系的集合可以使用默认方法stream()生成流
//List集合
ArrayList<String> list = new ArrayList<>();
Stream<String> listStream = list.stream();
//Set集合
HashSet<String> setStream = new HashSet<>();
//Map体系的集合间接生成流
Map<String,Integer> map = new HashMap<>();
//键流
Stream<String> KeyStream = map.keySet().stream();
//值流
Stream<Integer> valueStream = map.values().stream();
//键值对对象流
Stream<Map.Entry<String, Integer>> entryStream = map.entrySet().stream();
//数组可以通过Stream接口的静态方法of(T…values)生成流。
String[] strArray = {"hello","world","java"};
Stream<String> strArrayStream = Stream.of(strArray);
Stream<String> stringStream = Stream.of("hello", "today");
Stream<Integer> integerStream = Stream.of(1, 2, 3);
}
}
Stream流常见中间操作方法
Stream
Predicate接口中的方法 boolean test(T t):对给定的参数进行判断,返回一个布尔值。
Stream
Stream
static
Stream
Stream
Stream
Function接口中的方法 R apply(T t)
IntStream mapToInt(ToIntFunction mapper):返回一个InsStream其中包含将给定函数应用于此流的元素的结果。
IntStream: 表示原始int流
ToIntFunction接口中的方法 int applyAsInt(T value)
//将第一个字是王,并且长度为三的数据输出
arrayList.stream().filter(s -> s.startsWith("王"))
.filter(s -> s.length()==3).forEach(System.out::println);
System.out.println("--------------------------------------------------");
//跳过前5个元素,并且输出剩下数据的前2个元素
arrayList.stream().skip(5).limit(2).forEach(System.out::println);
输出结果:
王悉知
王哦知
王修知
--------------------------------------------------
张三丰
刘禹锡
Stream<String> limit = arrayList.stream().filter(s -> s.startsWith("王")).limit(5);
Stream<String> limit1 = arrayList.stream().skip(3).filter(s -> s.startsWith("王")).limit(2);
//将流1和流2合并,去重并输出。
Stream.concat(limit,limit1).distinct().forEach(System.out::println);
输出结果:王悉知
王知
王悉
王哦知
王修知
import java.util.ArrayList;
import java.util.stream.Stream;
public class Main {
public static void main(String[] args){
ArrayList<String> arrayList = new ArrayList<>();
arrayList.add("10");
arrayList.add("20");
arrayList.add("30");
arrayList.add("40");
arrayList.add("50");
//将集合中的字符串数据转换为整数之后在控制台输出
arrayList.stream().map(s -> Integer.parseInt(s)).forEach(System.out::println);
arrayList.stream().map(Integer::parseInt).forEach(System.out::println);
arrayList.stream().mapToInt(Integer::parseInt).forEach(System.out::println); //IntStream mapToInt(ToIntFunction mapper)
//对集合中的元素求和 必须是IntStream 才有求和方法
int sum = arrayList.stream().mapToInt(Integer::parseInt).sum();
System.out.println(sum);
}
}
Stream流的常见终结操作方法
void forEach(Consumer action):对此流的每个元素进行操作
Consumer接口中的方法 void accept(T t):对给定的参数执行此操作
long count():返回此流中的元素数
//统计集合中有几个王开头的元素
long count = arrayList.stream().filter(s -> s.startsWith("王")).count();
System.out.println(count);
Stream流的收集操作
对数据使用的Stream流的方式操作完毕后,我想把流中的数据收集到集合中,该怎么办呢?
Stream流的收集方法:
R collect(Collector collector), 但这个收集方法的参数是一个Collcetor接口。
工具类Collections提供了具体的收集方式
public static
public static
public static Collector toMap(Function keyMappr,Function valueMapper):把元素收集到Map集合中
类加载
反射
java反射机制:是指在运行时去获取一个类的变量和方法信息。然后通过获取到的信息来创建对象,调用方法的一种机制。由于这种动态性,可以极大的增强程序的灵活性,程序不用在编译期就完成确定,在运行期间仍然可以扩展。
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· Manus的开源复刻OpenManus初探
· AI 智能体引爆开源社区「GitHub 热点速览」
· 三行代码完成国际化适配,妙~啊~
· .NET Core 中如何实现缓存的预热?