一、Java简介
1.什么是Java
Java是一门面向对象编程语言,不仅吸收了C++语言的各种优点,还摒弃了C++里难以理解的多继承、指针等概念,因此Java语言具有功能强大和简单易用两个特征。
2.Java可以做什么
Java可以编写桌面应用程序、Web应用程序、分布式系统和嵌入式系统应用程序等
3.Java语言的特性
- 语法有点像C
- 去掉了指针,没有内存管理
- 真正的可移植性,编写一次,导出运行
- 面向对象
- 类型安全
- 高质量的类库
4.Java2平台的3个版本
JavaME(Java2 Micro Edition )Java2平台的微型版,应用于移动、无线及有限资源的环境。
JavaSE (Java2 Standard Edition)Java2平台的标准版,应用于桌面环境。
JavaEE (Java2 Enterprise Edition)Java2平台的企业版,应用于基于Java的应用服务器。
5.Java的三高是哪三高?
高可用、高性能、高并发。
6.了解JDK、JRE、JVM
JDK (Java Development Kit) Java开发工具
JRE (Java Runtime Environment)Java运行时环境
JVM (Java Virtual Machine)Java虚拟机
一般我们要学习Java,只需要下载JDK即可。里面就包含了JRE与JVM
二、Java的运行机制
1、认识编译型与解释型语言概述
打一个比方:一个不懂中文的外国人想要看懂一本中文书籍。编译型语言就是将这本书编译成一本英文书籍给这位外国人看,而解释型语言就是请一个翻译解释给外国人听。
编译型语言是指程序在执行之前需要一个专门的编译过程,把程序源文件编译为机器语言的文件,运行时不需要重新编译,执行效率高,但缺点是,编译型语言依赖编译器,跨平台性差。
解释型语言是指源代码不需要预先进行编译,在运行时,要先进行解释再运行。解释型语言执行效率低,但跨平台性好。
注意:编译型语言与解释型语言不同之处就在于两者的翻译的时机不同。
2、Java程序的运行机制是怎样的呢?
(1)编写.java文件
(2)javac命令将java文件编译成一个.class的文件
(3)java命令运行我们的class文件
详情如图示:
三、Java的数据类型
Java的数据类型分为两大类
1.数据类型 (primitive type)
数据类型又分为两大类,数值类型和布尔类型,如图所示:
2.引用类型 (reference type)
Java操作数据类型时的注意事项
1.使用进制定义整数类型,如下代码示例:
public static void main(String[] args) {
//进制数
int i = 10;
int i2 = 010;
int i3 = 0x10;
System.out.println(i);//10进制 结果为10
System.out.println(i2);//8进制,以"0"开头 结果为8
System.out.println(i3);//16进制,以"0x"开头 结果为16
}
2.不同数据类型的值进行比较会有可能出现错误。
public static void main(String[] args) {
//不同数据类型的值进行比较
float f = 0.1f;
double d = 0.1;
System.out.println(f == d);
}
程序的最终输出结果为:false,所以为了保证数据的正确性,我们应该避免使用不同类型的数据进行比较。
3.使用BigDecimal替代float。
float数值进行比较时,可能会出错。Float是一个离散的数值,它存在舍入误差。
public static void main(String[] args) {
//float类型的值进行比较
float f0 = 12345678678f;
float f1 = 12345678678f + 1;
System.out.println(f0 == f1);//最终输出结果为:true
}
4.Java数据类型转换的原则
容量大的数据类型转换为容量小的数据类型需要通过强制转换,且转换的过程中容易出现内存溢出和精度丢失的问题。例:
public static void main(String[] args) {
/*强制类型转换 double——int*/
double d0 = 123;
int i0 = (int)d0;//大的数据类型转换成小的数据类型需要强制转换
double d1 = 123.4;
int i1 = (int)d1;
System.out.println(i1);//强制转换出现精度丢失,结果为:123
/*int——byte*/
int i2 = 128;
byte b = (byte)i2;
System.out.println(b);//强制转换出现内存溢出,结果为-:128。这是因为byte的最大表示值为127,128超过了byte的最大表示值。
}
容量小的数据类型转换为容量大的数据类型则可以进行自动转换。
public static void main(String[] args) {
/*自动类型转换 int——double*/
int j = 8;
double k = j;
}
扩展:强类型语言与弱类型语言?
强类型语言
对数据类型的要求非常严格,变量必须要先声明类型才能使用
,如Java语言就是一门强类型语言。强类型语言更加安全,但效率低。
而弱类型语言
对数据类型的要求就不那么严格,变量可以不先声明类型就能使用
,弱类型语言有JavaScript。弱类型语言效率高,但不安全。
四、Java运算符
Java语言支持如下运算符:
- 算术运算符:+ ,-,*,/,%
- 赋值运算符:=
- 关系运算符:>,< ,>=,<=,==,!= instanceof
- 逻辑运算符 &&,||,!
- 位运算符:&,|,^,~,>>,<<,>>>(了解即可)
- 条件(三元)运算符:? :
- 扩展赋值运算符:+=,-=,*=,/=
一些简单的运算符这里就不做过多描述。
1.算术运算符 %
% 用于做取模运算,也就是取两数的相除的余数。
int a = 10;
int b = 3;
System.out.println(a % b);//最终结果为:1
2.关系运算符 == != instanceof
== 用于比较两个值是否相等。需要注意的它比较的是内存地址,而equals比较的就是值。
!= 用于判断两个是否不相等
instanceof 用于比较两个类是否属于同一个类型
3.逻辑运算符 && || !
&& 短路与,|| 短路或。用于做布尔值的运算。
!用于做取反运算。
&&口诀:同真为真,有假为假。
||口诀:同假为假,有真为真。
4.条件运算符 ? :
又称三元运算符 格式为:布尔表达式 ? 表达1 : 表达式2。
int a = 2;
int b = 2;
System.out.println(a == b ? "a等于b":"a不等于b");
自增、自减与Math类
自增++与自减- -
设int a = 0,++a 的结果等于 a + 1,- -a 等于 a - 1。
需要注意是,++a是先自增在取值,而a++是先取值在自增。
a++ + 2 = 2
++a + 2 = 3
Math类是Java操作数据进行运算的工具,它提供了许多有用的运算方法。
System.out.println(Math.abs(-1));//计算-1的绝对值
五、Java变量、常量及命名规范
变量就是一种可变的,用于存储数据的量,它包括局部变量,全局变量,与类变量。局部变量只作用在它所在的块作用域中,且局部变量必须先声明并赋值之后才可以使用。全局变量必须创建类的实例才可以使用。类变量是由static修饰的。
常量是使用final修饰的不可变的量。
变量的命名规范
- 所有变量、方法、类名:见名知意
- 类成员变量:驼峰命名 monthSalary
- 局部变量:驼峰命名
- 常量:字母全部大写,单词以下划线分隔 MAX_VALUE
- 类名:首字母大写和驼峰命名 DetailUser
- 方法名:驼峰命名 run() runStart()
六、反射与注解
注解
一、什么是注解?
注解(Annotation),不是程序本身,但是可以对程序做出解释,且可以被其他程序(比如:编译器等)读取。
注解的格式为"@注释名"在代码中存在的,还可以添加一些参数值。例如:@SuppressWarnings(value=“unchecked”);。
注解可以用于pagekage、class、method、filed等上面。相当于给他们添加了额外的辅助信息,我们可以通过反射机制编程实现对这些元数据的访问。
二、什么是内置注解
@Override:此注释只适用于修饰方法,表示一个方法声明打算重写超类中的另一个方法声明。
@Deprecated:用于修饰方法,表示不鼓励使用的方法。通常是因为它很危险或存在更好的选择。
@SuppressWarnings:用于抑制警告信息。需要参数SuppressWarnings(“all”)
三、元注解
元注解的作用是负责注解其他注解。
@Target:用于描述注解的使用范围(即:被描述的注解可以用在什么地方)
@Retention:表示需要在什么级别保存该注释信息,用于描述注解的生命周期。
@Document:说明该注解将被包含在javadoc中
@Inherited:说明子类可以继承父类中的该注解
package annotation;
import java.lang.annotation.*;
@MyAnnotation
public class Test {
@MyAnnotation
public void test(){
}
}
//定义一个注解
//Target 表示我们的注解可以用在那些地方
@Target(value = {ElementType.METHOD,ElementType.TYPE})
//Retention 表示我们的注解在什么地方还有效@Retention(value = RetentionPolicy.RUNTIME)
//Documented 表示是否将我们的注解生成在JAVAdoc中@Documented//Inherited 子类可以继承父类的注解
@Inherited@interface MyAnnotation{
}
四、自定义注解
自定义注解使用@interface
格式为:
public @intergace 注解名{
内容
}
其中的每一个方法实际上是声明了一个配置参数,方法的名称就是参数的名称。
返回值类型就是参数的类型(返回值只能是基本类型,Class,String,enum)
可以通过default来声明的默认值。如果只有一个参数成员,一般参数名为value
注解元素必须要有值,我们定义注解元素时,经常使用空字符串,0作为默认值。
反射
一、静态语言 VS 动态语言
动态语言是一类在运行时可以改变其结构
的语言:例如新的函数、对象、甚至代码可以被引进,已有的函数可以被删除或是其他结构上的变化。通俗点说就是在运行时代码可以根据某些条件改变自身结构。
主要动态语言:Object-C、C#、JavaScript、PHP、Python等。
静态语言与动态语言相对,运行时结构不可变
。如Java、C、C++。
Java不是动态语言,但可以称为"准动态语言"。即Java有一定的动态性,我们可以利用反射机制获得类似动态语言的特性。Java的动态性让编程的时候更加灵活!
二、反射的概念
Reflection(反射)是Java被视为动态语言的关键,反射机制允许程序在执行期借助于Reflection API 取得任何类的内部信息,并能直接操作任意对象的内部属性及方法。
加载完类之后,在堆内存的方法区中就产生了一个Class类型的对象(一个类只有Class对象),这个对象就包含了完整的类的结构信息。我们可以通过这个对象看到类的结构。这个对象就像一面镜子,透过这个镜子看到类的结构,所以,我们形象的称之为:反射。
三、如何获得反射的对象
package reflect;//什么叫反射
public class TestReflect {
public static void main(String[] args) throws
ClassNotFoundException {
Class c1 = Class.forName("reflect.User");
System.out.println(c1);
//一个类在内存中只有一个Class对象
//一个类被加载后,类的整个结构都会被封装在Class对象中。
Class c2 = Class.forName("reflect.User");
Class c3 = Class.forName("reflect.User");
Class c4 = Class.forName("reflect.User");
System.out.println(c2.hashCode());
System.out.println(c3.hashCode());
System.out.println(c4.hashCode());
}
}
class User{
private String name = "牛牛";
private int age = 10;
private int id = 1;
public User(){
}
public User(String name, int age, int id) {
this.name = name;
this.age = age;
this.id = id;
}
@Override
public String toString() {
return "User{" +
"name='" + name + '\'' +
", age=" + age +
", id=" + id +
'}';
}
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 int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
}
一个类在内存中只有一个Class对象。一个类被加载后,类的结构都会被封装到这个类的Class对象中。
四、Class类
对象照镜子后可以得到的信息:某个类的属性、方法和构造器、某个类到底实现了哪些接口。对于每个类而言,JRE都为其保留一个不变的Class类型的对象。一个Class对象包含了特定某个结构(class/interface/enum/anootaion/primitive type/void/[])的有关信息。
1.概述:
(1)Class本身也是一个类。
(2)Class对象只能由系统建立对象。
(3)一个加载的类在JVM中只会有一个Class实例。
(4)一个Class对象对应的是一个加载到JVM中的一个.class文件。
(5)每个类的实例都会记得自己是由哪个Class实例所生成。
(6)通过Class可以完整地得到一个类中的所有被加载的结构。
(7)Class是Reflection的根源,针对任何你想动态加载、运行的类,唯有先获得相应的Class对象。
2.Class类常用方法:
static ClassforName(String name); //返回指定类名name的Class对象
Object newInstance(); //调用缺省构造函数,返回Class对象的一个实例
getName(); //返回此Class对象的父类的Class对象
Class[] getinterfaces(); //获取当前Class对象的接口
ClassLoader getClassLoader(); //返回一个包含某些Constructor对象的数组
Method getMothoed(String name,Class… T); //返回一个Method对象,此对象的形参类型为paramType
Field[] getDeclaredFields();//返回Field对象的一个数组
3.哪些类可以有Class对象:
class:外部类,成员(成员内部类,静态内部类),局部内部类,匿名内部类。
interface:接口
[]:数组
enum:枚举
annotation:注解@interface
primitive type:基本数据类型
void
4.类的加载过程与ClassLoader的理解
(1)加载:将class文件字节码加载到内存,并将这些静态数据转换成方法区的运行时数据结构,然后生成一个代表这个类的java.lang.Class
(2)链接:将Java类的二进制代码合并到JVM的运行状态之中的过程
验证:确保加载的类星系符合JVM规范,没有安全方面的问题
准备:正式为类变量(static)分配内存并设置变量默认初始值得阶段,这些内存都将在方法区中进行分配。
解析:虚拟机常量池内的符号引用(常量名)替换为直接引用(地址d的过程。
(3)初始化:
执行类构造器() 方法的过程。类构造器()方法是由编译期自动收集类中所有类变量的赋值动作和静态代码块中的语句合并产生的。(类构造器是构造类信息的,不是构造该类对象的构造器)。
当初始化一个类的时候,如果发现其父类还没有进行初始化,则需要先触发器父类的初始化。
虚拟机会保证一个类的()方法在多线程环境中被正确加锁和同步。
package classloader;public class Test05 {
public static void main(String[] args) {
System.out.println(A.m);
}
}
class A{
static {
System.out.println("A类静态代码块初始化");
m = 300;
}
static int m = 100;
public A(){
}
}
/*解答:A的结果最终为100。这里的运行过程类似Javascript的变量提升。m变量先被初始化,执行过程应该是:clinit(){ int m; m = 300; m = 100;}*/
类的主动引用与被动引用(子父类的关系)
package classloader;
public class Test06 {
static {
System.out.println("Main类被加载");
}
public static void main(String[] args) {
//1.子类的初始化,会主动引用父类的初始化
// Son son = new Son();
//2.子类直接使用父类的静态变量,不会引用子类的初始化
System.out.println(Son.b);
//3.开辟一个空间,不会初始化任何对象
Son[] sons = new Son[10];
}
}
class Father{
static int b = 2;
static {
System.out.println("父类被加载");
}
}
class Son extends Father{
static {
System.out.println("子类被加载");
m = 300;
}
static int m = 100;
static final int M = 1;
}
/*
类的主动引用(一定会发生类的初始化)
当虚拟机启动,先初始化main方法所在的类。
new一个类的对象
调用类的静态成员(除了final常量)和静态方法
使用java.lang.reflect包的方法对类进行反射调用
当初始化一个类,如果其父类没有被初始化,则先会初始化它的父类。
类的被动引用(不会发生类的初始化)
当访问一个静态域时,只有真正声明这个域的类才会被初始化。如:当通过子类引用父类的静态变量,不会导致子类初始化。
通过数组定义类引用,不会触发此类的初始化。
引用常量不会触发此类的初始化(常量在链接阶段就存入调用类的常量池中了)
*/
五、使用反射获取运行时类的完整结构
Field、Method、Constructor、Superclass、Interface、Annotation
实现的全部接口
所继承的父类
全部的构造器
全部的方法
全部的Field(属性)
注解
package classloader;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
public class Test08 {
public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException, NoSuchMethodException {
Class c1 = Class.forName("classloader.User");
// User user = new User();
// c1 = user.getClass();
//获得类的名字
System.out.println(c1.getName());
//获得包名 + 类名
System.out.println(c1.getSimpleName());
//获得类名
//获得类的属性
Field[] fields = c1.getFields();//只能找到public的属性
for (Field field : fields) {
System.out.println(field);
}
System.out.println("=================");
fields = c1.getDeclaredFields();
//可以找到所有声明的属性
for (Field field : fields) {
System.out.println(field);
}
//获得指定属性的类全名
Field name = c1.getDeclaredField("name");
System.out.println(name); //获得类的方法
System.out.println("================");//获得类的公共方法
Method[] methods = c1.getMethods();
for (Method method : methods) {
System.out.println("normal:" + method);
}
methods = c1.getDeclaredMethods();//获得自定义的方法
for (Method method : methods) {
System.out.println("getDeclaredMethods:" + method);
}
//获得指定方法的映射
Method method = c1.getMethod("test",null);
System.out.println("指定方法:" + method);
//获得对象的构造器
System.out.println("=======================");
Constructor[] constructors = c1.getConstructors();
for (Constructor constructor : constructors) { System.out.println(constructor);
}
constructors = c1.getDeclaredConstructors();
for (Constructor constructor : constructors) {
System.out.println(constructor);
}
//获得指定的构造器
Constructor declaredConstructor = c1.getDeclaredConstructor(int.class);
System.out.println("指定构造器:" + declaredConstructor);
}
}
class User {
private static String name = "小明";
private int id;
private int age;
public int aa;
public User(int id) {
this.id = id;
}
public User() {
}
public void test() {
}
}
六、使用反射动态创建对象
package classloader;import pojo.User;import java.lang.reflect.Constructor;import java.lang.reflect.Field;import java.lang.reflect.InvocationTargetException;import java.lang.reflect.Method;
//动态的创建对象,通过反射
public class Test09 {
public static void main(String[] args) throws Exception { //获得Class对象
Class<?> c1 = Class.forName("pojo.User");
//构造一个对象
// User user = (User) c1.newInstance();
// System.out.println(user);
//通过构造器创建对象
// Constructor<?> declaredConstructor = c1.getDeclaredConstructor(String.class, int.class, int.class);
// User user2 = (User) declaredConstructor.newInstance("秦疆", 1, 1);
// System.out.println(user2);
//通过反射调用普通方法 User user3 = (User) c1.newInstance();
Method method = c1.getDeclaredMethod("test",String.class);
//invoke : 激活的意思
// (对象 , "方法的值")
method.invoke(user3,"牛牛");
//通过反射操作属性
System.out.println("*************");
User user4 = (User) c1.newInstance();
Field name = c1.getDeclaredField("name");
name.setAccessible(true);
//不能直接操作私有属性,我们需要关闭程序的安全检测,属性或者方法的setAccessible(true)
name.set(user4,"狂神");
System.out.println(user4.getName());
}
}
setAccessible方法是Method与Field、Constructor对象共有的。setAccessible设置为true为关闭访问安全检查,为false为开启安全检查。设置为true可以使原来无法访问的私有成员也可以访问。没有特殊要求的话可以关闭安全检测,这样可以提高系统的使用性能。
七、使用反射获取泛型信息
package classloader;import pojo.User;import java.lang.reflect.Method;import java.lang.reflect.ParameterizedType;import java.lang.reflect.Type;import java.util.List;import java.util.Map;
//通过反射获取泛型
public class Test11 {
public void test1(Map<String, User> map, List<User> list){
System.out.println("test01");
}
public Map<String,User> test2(){
System.out.println("test02");
return null;
}
public static void main(String[] args) throws NoSuchMethodException {
Method test1 = Test11.class.getMethod("test1", Map.class, List.class);
for (Type genericParameterType :
test1.getGenericParameterTypes()) {
System.out.println("#" + genericParameterType);
if(genericParameterType instanceof ParameterizedType){
Type[] actualTypeArguments = ((ParameterizedType) genericParameterType).getActualTypeArguments(); for (Type actualTypeArgument : actualTypeArguments) {
System.out.println(actualTypeArgument); }
}
}
test1 = Test11.class.getMethod("test2",null);
Type genericReturnType = test1.getGenericReturnType();
System.out.println(genericReturnType);
if(genericReturnType instanceof ParameterizedType){
Type[] actualTypeArguments = ((ParameterizedType) genericReturnType).getActualTypeArguments();
for (Type actualTypeArgument : actualTypeArguments) {
System.out.println(actualTypeArgument); }
}
}
}
此方法可以获取方法返回值与参数
八、使用反射获取注解的信息
package classloader;
import java.lang.annotation.*;
import java.lang.reflect.Field;
//练习反射操作注解
public class Test12 {
public static void main(String[] args) throws ClassNotFoundException, NoSuchFieldException {
Class<?> c1 = Class.forName("classloader.Student2"); //通过反射获得注解
Annotation[] annotations = c1.getAnnotations();
for (Annotation annotation : annotations) {
System.out.println(annotation);
}
//获得注解的value的值
TableStudent tableStudent = (TableStudent) c1.getAnnotation(TableStudent.class);
String value = tableStudent.value();
System.out.println(value);
//获得类指定的注解
Field name = c1.getDeclaredField("name");
FieldStudent annotation = name.getAnnotation(FieldStudent.class);
System.out.println(annotation.columnName());
System.out.println(annotation.type());
System.out.println(annotation.length());
}
}
@TableStudent("db_student")
class Student2{
@FieldStudent(columnName = "db_id",type = "int",length = 10)
private int id;
@FieldStudent(columnName = "db_age",type = "int",length = 10)
private int age;
@FieldStudent(columnName = "db_name",type = "varchar",length = 3)
private String name;
public Student2() {
}
@Override
public String toString() {
return "Student2{" +
"id=" + id +
", age=" + age +
", name='" + name + '\'' +
'}';
}
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Student2(int id, int age, String name) {
this.id = id;
this.age = age;
this.name = name;
}
}
//类的注解
@Target(ElementType.TYPE
)
@Retention(RetentionPolicy.RUNTIME)
@interface TableStudent{
String value();
}
//属性的注解
@Target(ElementType.FIELD)@Retention(RetentionPolicy.RUNTIME)
@interface FieldStudent{
String columnName();
String type();
int length();
}
七、IO流
IO流是一种数据的流动,按照流动的方向,以内存为基准,分为输入input 和输出output ,即流向内存是输入流,流出内存的输出流。Java中I/O操作主要是指使用java.io包下的内容,进行输入、输出操作。输入也叫做读取数据,输出也叫做作写出数据。查看IO流更多详细笔记史上最骚最全最详细的IO流教程,没有之一!
IO的分类根据数据的流向分为:输入流 和 输出流。
- 输入流 :把数据从其他设备上读取到内存中的流。
- 输出流 :把数据从内存中写出到其他设备上的流。
根据数据的类型分为:字节流 和 字符流。
- 字节流 :以字节为单位,读写数据的流。
- 字符流 :以字符为单位,读写数据的流。
分类之后对应的超类(即父类),这四个类的子类名称基本都是以其父类名作为子类名的后缀。
流 | 超类 |
---|---|
字节输入流 | InputStream |
字节输出流 | OutputStream |
字符输入流 | Reader |
字符输出流 | Writer |
IO流笔记内容
- File类
- 字节流
- 字符流
- 缓冲流
- 转换流
- 序列化流
File类
java.io.File 类是专门对文件进行操作的类,只能对文件本身进行操作,不能对文件内容进行操作。它是文件和目录路径名的抽象表示,主要用于文件和目录的创建、查找和删除等操作。
1、File对象的构造方法
public File(String pathname) :通过将给定的路径名字符串转换为抽象路径名来创建新的 File实例。
public File(String parent, String child) :从父路径名字符串和子路径名字符串创建新的 File实例。
public File(File parent, String child) :从父抽象路径名和子路径名字符串创建新的 File实例。
1. 一个File对象代表硬盘中实际存在的一个文件或者目录。
2. File类构造方法不会给你检验这个文件或文件夹是否真实存在,因此无论该路径下是否存在文件或者目录,都不影响File对象的创建。
// 文件路径名
String path = "D:\\123.txt";
File file1 = new File(path);
// 文件路径名
String path2 = "D:\\1\\2.txt";
File file2 = new File(path2); -------------相当于D:\\1\\2.txt
// 通过父路径和子路径字符串
String parent = "F:\\aaa";
String child = "bbb.txt";
File file3 = new File(parent, child); --------相当于F:\\aaa\\bbb.txt
// 通过父级File对象和子路径字符串
File parentDir = new File("F:\\aaa");
String child = "bbb.txt";
File file4 = new File(parentDir, child); --------相当于F:\\aaa\\bbb.txt
2、File对象操作方法
File f = new File("d:/aaa/bbb.java");
System.out.println("文件绝对路径:"+f.getAbsolutePath());
System.out.println("文件构造路径:"+f.getPath());
System.out.println("文件名称:"+f.getName());
System.out.println("文件长度:"+f.length()+"字节");
File f2 = new File("d:/aaa");
System.out.println("目录绝对路径:"+f2.getAbsolutePath());
System.out.println("目录构造路径:"+f2.getPath());
System.out.println("目录名称:"+f2.getName());
System.out.println("目录长度:"+f2.length());
输出结果:
文件绝对路径:d:\aaa\bbb.java
文件构造路径:d:\aaa\bbb.java
文件名称:bbb.java
文件长度:2116字节
目录绝对路径:d:\aaa
目录构造路径:d:\aaa
目录名称:aaa
目录长度:3236
3、创建一个新的文件
File file = new File("F://aa//bb//xx.txt");
if (!file.exists()) {
file.getParentFile().mkdirs();
file.createNewFile();
}
4、目录的遍历
public String[] list() :返回一个String数组,表示该File目录中的所有子文件或目录。
public File[] listFiles() :返回一个File数组,表示该File目录中的所有的子文件或目录。
File dir = new File("G:\光标");
//获取当前目录下的文件以及文件夹的名称。
String[] names = dir.list();
for(String name : names){
System.out.println(name);
}
//获取当前目录下的文件以及文件夹对象,只要拿到了文件对象,那么就可以获取更多信息
File[] files = dir.listFiles();
for (File file : files) {
System.out.println(file);
}
5、使用递归函数遍历所有的文件
public static void Recursion(File file){
//1、判断传入的是否是目录
if(!file.isDirectory()){
//不是目录直接退出
return;
}
//已经确保了传入的file是目录
File[] files = file.listFiles();
//遍历files
for (File f: files) {
//如果该目录下文件还是个文件夹就再进行递归遍历其子目录
if(f.isDirectory()){
//递归
Recursion(f);
}else {
//如果该目录下文件是个文件,则打印对应的名字
System.out.println(f.getName());
}
}
}
字节流
文件的世界里一切皆为字节
我们必须明确一点的是,一切文件数据(文本、图片、视频等)在存储时,都是以二进制数字的形式保存,都一个一个的字节,那么传输时一样如此。所以,字节流可以传输任意文件数据。在操作流的时候,我们要时刻明确,无论使用什么样的流对象,底层传输的始终为二进制数据。
一、字节输出流 OutputStream
首先要了解一点,OutputStream是一个抽象的类,同时是表示字节输出流的所有类的超类。
它提供了字节输出流的基本共性功能方法:
1、 public void close() :关闭此输出流并释放与此流相关联的任何系统资源。
2、 public void flush() :刷新此输出流并强制任何缓冲的输出字节被写出。
3、 public void write(byte[] b):将 b.length个字节从指定的字节数组写入此输出流。
4、 public void write(byte[] b, int off, int len) :从指定的字节数组写入 len字节,从偏移量 off开始输出到此输出流。 也就是说从off个字节数开始读取一直到len个字节结束
5、 public abstract void write(int b) :将指定的字节输出流。
以上五个方法则是字节输出流都具有的方法,由父类OutputStream定义提供,子类都会共享以上方法
字节输出流OutputStream的一个实现类(子类)是FileOutputStream文件输出流,用于将数据写出到文件。它的具体使用这里不做详述了。
二、字节输出流 InputStream
InputStream抽象类是表示字节输入流的所有类的超类(父类),可以读取字节信息到内存中。
它提供了字节输入流的基本共性功能方法:
1、 public void close() :关闭此输入流并释放与此流相关联的任何系统资源。
2、 public abstract int read(): 从输入流读取数据的下一个字节。
3、 public int read(byte[] b): 该方法返回的int值代表的是读取了多少个字节,读到几个返回几个,读取不到返回-1
它的一个是实现(子类)FileInputStream文件输入流,用于从文件中读取字节。
案例:使用字节流将文本写入文件
File file = new File("D://1/2/33.txt");
if(!file.exists()) {
file.getParentFile().mkdirs();
//file.createNewFile();
}else {
//文件存在则打印文件信息
System.out.println(file.getName());
System.out.println(file.getAbsolutePath());
System.out.println(file.getPath());
//lastModified():long 最后修改时间
System.out.println(new Date(file.lastModified()));
//length():int 文件的大小,字节
System.out.println(file.length());
}
//写入内容
OutputStream out = new FileOutputStream(file,true);
//从控制台接收一段文字写入到文件中
Scanner input = new Scanner(System.in);
System.out.println("写日志(exit退出):");
while(true) {
String str = input.next();
if("exit".equals(str.trim())) {
break;
}
//字符串转换成byte[]
byte[] arr = str.getBytes();//转换成字节的时候可以携带编码方式
out.write(arr);
}
out.close();//关闭资源
字符流
字符流的由来:因为数据编码的不同,因而有了对字符进行高效操作的流对象,字符流本质其实就是基于字节流读取时,去查了指定的码表,而字节流直接读取数据会有乱码的问题(读中文会乱码)。
简单的说字节流读取数据(如中文)时会存在乱码的问题,故为了解决乱码的问题字符流就应运而生了。
一、字符流Reader
Reader是一个抽象类,是字符输入流的所有类的超类(父类),它用于读取字符信息到内存中。
它定义了字符输入流的基本共性功能方法:
1、public void close() :关闭此流并释放与此流相关联的任何系统资源。
2、 public int read(): 从输入流读取一个字符。
3、 public int read(char[] cbuf): 从输入流中读取一些字符,并将它们存储到字符数组 cbuf中
Reader实现类有FileReader,详细用法可参考FileReader类的用法
二、字符输出流(Writer)
Writer也是一个抽象类,它是字符输出流的所有类的超类(父类),用于将指定的字符信息写出到目的地。
它同样定义了字符输出流的基本共性功能方法:
1、void write(int c) 写入单个字符。
2、void write(char[] cbuf)写入字符数组。
3、 abstract void write(char[] cbuf, int off, int len)写入字符数组的某一部分,off数组的开始索引,len写的字符个数。
4、 void write(String str)写入字符串。
5、void write(String str, int off, int len) 写入字符串的某一部分,off字符串的开始索引,len写的字符个数。
6、void flush()刷新该流的缓冲。
7、void close() 关闭此流,但要先刷新它。
Writer的实现类有FileWriter,详细用法可参考FileWriter类的用法
案例:从A文件读到B文件
/**字符流
* 比字节流要高级,可以读取char[]数组
* @author SMILE
*/
//创建输入流
File file = new File("D://1/2/33.txt");
if(!file.exists()) {
//不存在的情况
file.getParentFile().mkdirs();
file.createNewFile();
}
InputStream in = new FileInputStream(file);
File file2 = new File("D://1/2/55.txt");
//写的过程,不需要判断文件是否存在。写入操作会自动创建
OutputStream out = new FileOutputStream(file2,true);
//创建容量为20的数组用来装载读取的字节信息
byte[] arr = new byte[20];
//将A文件的信息读取到数组arr中。返回值len表示实际读取的长度
int len = 0;
while((len = in.read(arr))!=-1) {//len为-1表示读取到文件末端
//将读取的内容写入B文件。
//把数组arr中从0到len长度的字节写入B文件(只写入有效字符)
out.write(arr, 0, len);
//len = in.read(arr);//再次读取A文件的其他内容
}
//关闭流
out.close();
in.close();
缓冲流
缓冲流,也叫高效流,是对4个FileXxx 流的“增强流”。
缓冲流的基本原理:
1、使用了底层流对象从具体设备上获取数据,并将数据存储到缓冲区的数组内。
2、通过缓冲区的read()方法从缓冲区获取具体的字符数据,这样就提高了效率。
3、如果用read方法读取字符数据,并存储到另一个容器中,直到读取到了换行符时,将另一个容器临时存储的数据转成字符串返回,就形成了readLine()功能。
也就是说在创建流对象时,会创建一个内置的默认大小的缓冲区数组,通过缓冲区读写,减少系统IO次数,从而提高读写的效率。
缓冲书写格式为BufferedXxx,按照数据类型分类:
字节缓冲流:BufferedInputStream,BufferedOutputStream
字符缓冲流:BufferedReader,BufferedWriter
案例:使用缓冲流读取文件
/**缓冲流
* 比字符流要高级,可以提高读写的速度
* 缓冲流本质上是字符流的实现类
* 很多方法和字符流相似
* 一行一行的读 readLine()
* 先把数据读取到缓冲区,然后等缓冲区满了,可以一次性读完,效率更高
* @author SMILE
*/
//创建文件类对象
File file =new File("D:\\tr02302.txt");
if(!file.exists()) {
return;
}
Reader r = new FileReader(file);
BufferedReader br = new BufferedReader(r);
StringBuilder sb = new StringBuilder();
String line = null;//一行一行的读
while((line = br.readLine())!=null) {
sb.append(line);
//line = br.readLine();
}
System.out.println("读取的内容是:"+sb);
//关闭流
br.close();
r.close();
//输出缓冲流 构造方法的参数形式非常多
// PrintWriter pw = new PrintWriter("");
转换流
想要了解转换流,直接看图
点击了解编码与解码
字节流到字符流的桥梁 InputStreamReader类
转换流InputStreamReader是Reader的子类,从字面意思可以看出它是从字节流到字符流的桥梁。它读取字节,并使用指定的字符集将其解码为字符。它的字符集可以由名称指定,也可以接受平台的默认字符集。
字符流到字节流的桥梁 OutputStreamWriter类
OutputStreamWriter ,是Writer的子类,字面看容易混淆会误以为是转为字符流,其实不然,OutputStreamWriter为从字符流到字节流的桥梁。使用指定的字符集将字符编码为字节。它的字符集可以由名称指定,也可以接受平台的默认字符集。
案例:转换流的使用
/**转换流
* 字节流----[转换流]-----》缓冲流
* 字符流----------------》缓冲流
* @author SMILE
*
*/
//编辑器,模仿Scanner,按照预定的字符停止输入
//①字节流
InputStream in = System.in;
System.out.println("请输入你的内容:");
//②转换流,可以指定编码方式
InputStreamReader ir = new InputStreamReader(in);
//③缓冲流
BufferedReader br = new BufferedReader(ir);
//读取一行信息
String line = br.readLine();
StringBuilder sb =new StringBuilder();//大字符串追加器
while(!"exit".equals(line)) {
sb.append(line+"\n");
line = br.readLine();
}
System.out.println("输入终止,你输入的内容是:");
System.out.println(sb);
//关闭流
序列化流
Java对象序列化的机制
用一个字节序列可以表示一个对象,该字节序列包含该对象的数据、对象的类型和对象中存储的属性等信息。字节序列写出到文件之后,相当于文件中持久保存了一个对象的信息。
简单来说,对象序列化就是将对象转储为字节文件。反之,该字节序列还可以从文件中读取回来,重构对象,对它进行反序列化。对象的数据、对象的类型和对象中存储的数据信息,都可以用来在内存中创建对象。看图理解序列化:
java.io.ObjectOutputStream 类,将Java对象的原始数据类型写出到文件,实现对象的持久存储。
ObjectInputStream反序列化流,将之前使用ObjectOutputStream序列化的原始数据恢复为对象。
Java8的新特性
Lambda表达式操作集合元素
1、使用Iterable的方法优雅便捷的遍历集合元素
Java8中为集合的父类Iterable接口新增了一个forEach(Consumer action);默认方法,该方法所需参数类型是一个函数式接口。当程序调用 Iterable 的 forEach(Consumer action) 遍历集合元素时,程序会依次将集合元素传给 Consumer 的 accept(T t) 方法(该接口中唯一的抽象方法)。正因为 Consumer 是函数式接口,因此可以使用 Lambda 表达式来遍历集合元素。
public class CollectionEach {
public static void main(String[] args) {
// 创建一个集合
Collection objs = new HashSet();
objs.add("C语言中文网Java教程");
objs.add("C语言中文网C语言教程");
objs.add("C语言中文网C++教程");
// 调用forEach()方法遍历集合
objs.forEach(obj -> System.out.println("迭代集合元素:" + obj));
}
}
2、使用Predicate操作集合
Java8之后为集合新增了一个removeIf(Predicate filter)方法,用于批量删除符合filter条件(Predicate函数式接口)的所有元素。
public class ForeachTest {
public static void main(String[] args) {
// 创建一个集合
Collection objs = new HashSet();
objs.add(new String("C语言中文网Java教程"));
objs.add(new String("C语言中文网C++教程"));
objs.add(new String("C语言中文网C语言教程"));
objs.add(new String("C语言中文网Python教程"));
objs.add(new String("C语言中文网Go教程"));
// 使用Lambda表达式(目标类型是Predicate)过滤集合
objs.removeIf(ele -> ((String) ele).length() < 12);
System.out.println(objs);
}
3、使用Predicate函数自定义的集合统计方法
public class ForeachTest2 {
public static void main(String[] args) {
// 创建一个集合
Collection objs = new HashSet();
objs.add(new String("C语言中文网Java教程"));
objs.add(new String("C语言中文网C++教程"));
objs.add(new String("C语言中文网C语言教程"));
objs.add(new String("C语言中文网Python教程"));
objs.add(new String("C语言中文网Go教程"));
// 统计集合中出现“C语言中文网”字符串的数量
System.out.println(calAll(objs, ele -> ((String) ele).contains("C语言中文网")));
// 统计集合中出现“Java”字符串的数量
System.out.println(calAll(objs, ele -> ((String) ele).contains("Java")));
// 统计集合中出现字符串长度大于 12 的数量
System.out.println(calAll(objs, ele -> ((String) ele).length() > 12));
}
public static int calAll(Collection books, Predicate p) {
int total = 0;
for (Object obj : books) {
// 使用Predicate的test()方法判断该对象是否满足Predicate指定的条件
if (p.test(obj)) {
total++;
}
}
return total;
}
}
JDBC连接
SUN公司为了简化开发人员的(对数据库的统一)操作,提供了一个(Java操作数据库的)规范,俗称JDBC这些规范的实现由具体的厂商去做~
对于开发人员我们只需要掌握JDBC接口的操作即可。
JDBC连接使用案例
导入mysql数据库驱动
<dependency>
<groupId>mysql</groupId>
<artifactId>mysql-connector-java</artifactId>
<version>8.0.25</version>
</dependency>
编写数据库连接字段
url=jdbc:mysql://localhost:3306/mybatis?useUnicode=true&characterEncoding=utf8&useSSL=true
username=root
password=123456
driver=com.mysql.cj.jdbc.Driver
JDBCJ操作工具类
package com.kuang;
import java.io.InputStream;
import java.sql.*;
import java.util.Properties;
public class JdbcUtils {
private static String driver = null;
private static String url = null;
private static String username = null;
private static String password = null;
static {
try {
InputStream in = JdbcUtils.class.getClassLoader().getResourceAsStream("data.properties");
Properties properties = new Properties();
properties.load(in);
driver = properties.getProperty("driver");
url = properties.getProperty("url");
username = properties.getProperty("username");
password = properties.getProperty("password");
Class.forName(driver);
} catch (Exception e) {
e.printStackTrace();
}
}
//获取连接
public static Connection getConnection() throws SQLException {
return DriverManager.getConnection(url,username,password);
}
//释放连接资源
public static void release(Connection conn, Statement st, ResultSet rs){
if(rs != null){
try {
rs.close();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
if(st != null){
try {
st.close();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
if(conn != null){
try {
conn.close();
} catch (SQLException throwables) {
throwables.printStackTrace();
}
}
}
}
测试代码
public class TestJDBC {
public static void main(String[] args) {
Connection conn = null;
Statement st = null;
ResultSet rs = null;
try {
conn = JdbcUtils.getConnection();//获取数据库连接
st = conn.createStatement();//获取SQL执行对象
String sql = "INSERT INTO user value(null,'123','123','123')";
int i = st.executeUpdate(sql);
if (i > 0) {
System.out.println("插入成功");
}
sql = "SELECT * FROM user";
rs = st.executeQuery(sql);
while (rs.next()){
System.out.println(rs.getInt("id"));
System.out.println(rs.getString("name"));
System.out.println(rs.getString("pwd"));
System.out.println(rs.getString("perm"));
}
} catch (SQLException throwables) {
throwables.printStackTrace();
}finally{
JdbcUtils.release(conn,st,rs);
}
}
}
以上的连接有SQL注入的风险,一定要将Statement换成preparestatement
要在使用数据库操作中加入事务,这里只写关键语句。
conn.setAutoCommit(false);//关闭自动提交,开启事务
conn.commit();//提交事务
conn.rollback();//回滚
正则表达式匹配字符串
格式:String.matches(“regex”);
例:"stu我说了"matches(“stu(.*)”);