一、面向对象
1、概念
在程序中使用面向对象来映射现实中的事物,使用对象关系来描述事物之间的关系,这就是面向对象思想。 何为对象:万物皆对象 对象在程序中是new出来的 是怎么被new出来?先有类后有对象 类和对象有什么关系?对象所属于类 类:是一类事物的统称,是一个抽象的概念 对象:对象是实例也叫实体
2、三大特性
封装:隐藏实现细节,仅对外提供访问方式setter/getter方法 封装又称为私有的 关键字private 继承:让某个类型的对象获得另一个类型的对象的属性和方法。继承就是子类继承父类的特征和行为,使得子类对象(实例)具有父类的实例域和方法,或子类从父类继承方法,使得子类具有父类相同的行为。 多态:对于同一个行为,不同的子类对象具有不同的表现形式。 注意:编写类的时候可以在一个.java文件中写多个类,但是这些类只能有一个被public修饰的类,更可以分开写
3、代码演示
man类
public class Man {
private int money;
public void show(){
System.out.println(money);
}
//主要是给属性赋值
public void setMoney(int money){
this.money=money;
}
public int getMoney(){
return money;
}
}
测试类:
class Person{
String name;
int age;
public void eat(){
System.out.println("吃饭");
}
}
public class object1 {
public static void main(String[] args) {
//创建对象的过程
Person p = new Person();
p.eat();
Man man = new Man();
//创建对象的过程
man.show();
System.out.println(man.getMoney());
man.setMoney(100);
man.show();
System.out.println(man.getMoney());
}
}
二、继承
1、概念
父类(基类/超类):被继承的类。它定义了一组属性和方法,子类将从中继承这些特性。
子类(派生类/子类):从父类继承的类。它不仅继承了父类的属性和方法,还可以添加新的属性和方法,或者重写(覆盖)父类的方法。
2、继承的好处
代码重用:子类可以重用父类中已经实现的代码,从而避免重复编写相同的代码。
扩展性:子类可以扩展父类的功能,添加新的特性或方法,而无需修改父类代码。
易维护性:当需要修改父类的功能时,子类将自动继承这些改动,减少了维护工作的复杂性。
3、继承的实现
构造函数和析构函数:在子类构造对象时,通常会调用父类的构造函数以初始化从父类继承的属性。在对象销毁时,也会调用父类的析构函数以执行清理工作。
方法重写(覆盖):子类可以重写父类的方法,以提供不同的实现。重写的方法通常有相同的名称和参数列表。
方法调用:子类可以调用父类的方法,通过 super
关键字(在 Python 中)或使用父类名称(在 Java 和 C++ 中)来调用。
4、代码展示
Person
定义了成员变量 id
(整数类型)、skin
(字符串类型)和 eat
(字符串类型),并初始化了 id
为 10,skin
为 "yellow"
,eat
为 "eatting"
。定义了一个无参的 eat
方法,用于打印父类在吃饭的信息以及 id
的值。定义了一个接受整数参数 id
的构造函数,用于初始化 id
成员变量
public class Person {
public int id =10;
public String skin = "yellow";
public String eat="eatting";
public void eat(){
System.out.println("父类在吃饭"+id);
}
//Person类的构造函数,接受一个整数参数id,并赋值给成员变量id
public Person(int id){
this.id=id;
}
}
Man类
继承自 Person
类。
定义了一个 skin
方法,用于打印父类的 skin
成员变量的值。
重写了父类的 eat
方法,在调用父类的 eat
方法后,打印 "子类在 Eatting"。
定义了一个接受整数参数的构造函数,通过 super(id)
调用父类的构造函数来初始化父类的 id
,调用父类的 eat
方法,并将父类的 skin
成员变量修改为 "red"
。
public class Man extends Person{
//定义skin()方法
public void skin(){
System.out.println(skin);
}
/*
在面向对象编程中,重写(Override)是指子类对父类中已有的方法进行重新实现,以改变或扩展该方法的行为。
重写需要满足以下几个条件:
方法名、参数列表(包括参数的数量、类型和顺序)必须与父类中被重写的方法完全相同。
返回值类型与父类中被重写的方法相同或者是其子类(在 Java 5 及更高版本中,返回值类型可以是父类方法返回值类型的子类)。
访问修饰符不能比父类中被重写的方法更严格(例如,父类方法是 public,子类重写的方法不能是 private 或 protected)
*/
//重写
@Override
public void eat(){
//此处调用了父类中的方法eat(),在使用Man子类方法eat()时实现方法重写
super.eat();
System.out.println("子类在Eatting");
}
//Man中的一个构造函数,接收一个整数参数
public Man(int id){
//super调用了父类类的构造函数,给父类构造函数传参
super(id);
//调用的父类的eat()方法
super.eat();
//为父类的变量赋值
super.skin="red";
}
}
Woman类
继承自 Person
类。
重写了父类的 eat
方法,打印 "女人在" 加上父类的 eat
成员变量的值。
定义了一个接受整数参数的构造函数,通过 super(id)
调用父类的构造函数,并调用父类的 eat
方法。
public class women extends Person{
public void eat(){
System.out.println("女人在"+eat);
}
public women(int id){
super(id);
super.eat();
}
}
测试类
创建了一个 Man
对象 man
,并传递参数 1 给其构造函数。
调用 man
对象的 skin
方法。
创建了一个 women
对象 nv
,并传递参数 2 给其构造函数。
调用 nv
对象的 eat
方法。
调用 man
对象的 eat
方法,先执行父类的 eat
方法,再执行子类重写的 eat
方法。
public class Demon {
public static void main(String[] args) {
//创建Man类的对象,并传递参数1给其构造函数
Man man = new Man(1);
//调用Man中的skin()方法
//父类Person传参到Man,然后调用Man里面的skin()函数
man.skin();
//创建women类的对象,并传递参数给其构造函数
//父类Person传参到women,然后调用women里面的eat()函数
women nv = new women(2);
//调用women类中的eat()方法
nv.eat();
//调用Man类对象的eat()方法
//此处用到方法重载先调用父类Preson中的eat()方法,然后调用Man()里面的eat()方法
man.eat();
}
}
运行结果
5、Final关键字
5.1. final用于变量
当final用于变量时,它表示这个变量的值在初始化之后不能再被改变。根据变量类型的不同,final的行为有所不同:
基本数据类型(如int, double, char等):final变量的值在被初始化后无法更改。例如:
final int MAX_VALUE = 100;
引用数据类型(如对象的引用):final变量的引用不能被重新赋值,但是引用的对象本身的状态仍然可以被修改。例如:
final List<String> list = new ArrayList<>();
list.add("Hello"); // 这是允许的,因为修改对象的内容
// list = new ArrayList<>(); // 这是不允许的,因为不能改变引用的对象
5.2final用于方法
当final用于方法时,表示这个方法不能被子类重写(override)。这是用于防止方法的行为在继承过程中被改变。例如:
public class Parent {
public final void display() {
System.out.println("Display method in Parent class");
}
}
public class Child extends Parent {
// 以下代码会导致编译错误
// public void display() {
// System.out.println("Display method in Child class");
// }
}
5.3final用于类
当final用于类时,表示这个类不能被继承(subclass)。这是用来防止继承,从而避免子类改变其行为。例如:
public final class ImmutableClass {
// Class implementation
}
// 以下代码会导致编译错误
// public class ExtendedClass extends ImmutableClass {
// }
5.4final用于局部变量
在方法内部声明的局部变量使用final时,这些变量必须在声明时初始化,并且一旦赋值后,值不能再改变。例如:
public void method() {
final int localVar = 10;
// localVar = 20; // 这是不允许的,因为localVar是final的
}
5.5总结
final变量:其值不可变。对于基本数据类型,值不可更改;对于引用类型,引用不可更改但对象状态可以更改。
final方法:该方法不能被子类重写。
final类:该类不能被继承。
final局部变量:在方法中声明的局部变量,必须初始化且值不可更改。
三、构造函数
构造函数与类的区别: 类(Class): 类是一种用户自定义的数据类型,用于描述具有相同属性和行为的一组对象的模板。 类定义了对象的属性(成员变量)和方法(成员函数)。 一个类可以包含多个构造函数以及其他普通方法。 构造函数(Constructor): 1.构造函数是类中的特殊方法,用于对象的创建和初始化。 2.构造函数的名称与类名相同,没有返回值类型。 3.其主要目的是在创建对象时,为对象的成员变量赋予初始值,或者执行一些与对象初始化相关的必要操作。 4.每个类至少有一个构造函数,如果没有显式定义,Java 会提供一个默认的无参构造函数。
1、普通构造函数
实体类:
public class Person {
static String name;
static String sex;
static int age;
static int id;
//创建无参数构造方法
public Person(){
System.out.println("无参数构造方法");
}
//普通方法
public void method(){
System.out.println("普通方法"+name);
}
//有参函数构造,初始成员变量
public Person(String name,String sex,int age,int id){
this.name=name;
this.sex=sex;
this.age=age;
this.id=id;
}
}
测试类:
public class buildFunction {
public static void main(String[] args) {
//创建对象的过程,此过程调用默认的无参构造函数
Person p=new Person();
p.method();
//创建Person类的对象,调用有参的构造函数
Person p1 = new Person("赵云", "男", 1, 1);
p1.method();
}
}
2、静态以及优先级
Person类
static表示静态 static修饰的变量成为静态变量修饰的方法称为静态方法 特点: 1. 静态只能访问静态不能访问非静态 2. 静态不能和this,super关键字共存 3. 静态随着类的加载而加载 4. 静态可以由类名直接访问 优先级:静态代码块>构造代码块>构造函数>普通函数 静态代码块可以做数据共享 静态代码块只被执行一次
public class Person {
static {
System.out.println("静态代码块");
}
{
System.out.println("构造代码块");
}
public Person(){
System.out.println("构造函数");
}
public static void method(){
System.out.println("普通静态方法");
}
}
测试类
public class functionStatic {
public static void main(String[] args) {
//调用被执行
Person p1 = new Person();
Person p2= new Person();
Person p3 = new Person();
p1.method();//也可以用Person.method();
}
}
输出结果
四、抽象类
1、概念
在 Java 中,抽象类是一种不能被实例化的类,它可以包含抽象方法和非抽象方法。 以下是关于 Java 抽象类的一些关键特点和要点: 1.抽象方法:抽象类可以包含抽象方法。抽象方法是只有方法签名(方法名、参数列表和返回类型),但没有方法体的方法。抽象方法使用 abstract 关键字修饰,例如: public abstract void myAbstractMethod(); 。 2.不能实例化:由于抽象类可能包含未完全定义的方法(即抽象方法),所以不能直接创建抽象类的对象。 3.继承和实现:子类继承抽象类后,必须实现抽象类中的所有抽象方法,否则子类也必须声明为抽象类。 4.部分实现:抽象类可以提供部分方法的实现,为子类提供一些通用的功能和行为,子类可以选择重写这些方法或者直接使用父类的实现。 5.代码复用:通过将公共的属性和方法放在抽象类中,可以实现代码的复用,减少重复代码的编写。 6.设计和架构:抽象类有助于设计良好的类层次结构,它可以定义一些通用的规则和行为,使得整个系统的架构更加清晰和易于理解。
2、代码演示
animal类
//抽象类使用关键字 abstract 修饰。可以包含抽象方法和非抽象方法(普通方法)。
public abstract class animal {
//构造函数
public animal(){
System.out.println("父类(animal)的构造函数");
}
public abstract void method();
}
duck类
public class duck extends animal{
public duck(){
//调用了父类的无参构造函数
super();
}
public void method(){
System.out.println("子类覆盖抽象方法");
}
}
测试类
public class demon {
//抽象类不能被实例化,只能被子类覆盖实现
public static void main(String[] args) {
//animal a = new animal();
duck d =new duck();
//抽象方法只能通过子类覆盖实现
//animal b = new animal();
d.method();
}
}
五、接口
1、概念
关键字:interface 实现一个接口关键字:implements 特点:java中类不能多继承,但能多实现,接口中的方法都是抽象方法,接口中的变量都是常量 接口中的常量可以不用abstract方法,也可以不用写
2、代码演示
usb接口
public interface usb {
//抽象方法,接口里面都是抽象方法
public abstract void usbMethod();
//常量
public static final String STR="STR";
}
PowerSuppely接口
public interface PowreSuppply {
public abstract void powerMethod();
}
mouse类
public class mouse implements usb,PowreSuppply{
public void usbMethod(){
System.out.println("鼠标已插入");
}
public void powerMethod(){
System.out.println("鼠标电源已接入");
}
}
keyboard类
public class keyBoard implements usb,PowreSuppply{
public void usbMethod(){
System.out.println("键盘已插入");
}
public void powerMethod(){
System.out.println("键盘电源已接入");
}
}
测试类
public class demon {
public static void main(String[] args) {
keyBoard jianpan = new keyBoard();
jianpan.usbMethod();
mouse shubiao = new mouse();
shubiao.usbMethod();
jianpan.powerMethod();
shubiao.powerMethod();
System.out.println(usb.STR);
}
}
测试结果
3、多态性展示
Aniaml接口
public interface Animal {
public void runMethod();
}
Fish类
public class Fish implements Animal{
public void runMethod(){
System.out.println("鱼飞");
}
}
Dog类
public class Dog implements Animal{
public void runMethod(){
System.out.println("狗飞");
}
}
Bird类
public class Bird implements Animal{
public void runMethod(){
System.out.println("鸟飞");
}
}
测试类
public class Demon {
public static void main(String[] args) {
//父类引用指向子类对象
Animal dog = new Dog();
dog.runMethod();
Animal bird = new Bird();
bird.runMethod();
Animal fish = new Fish();
fish.runMethod();
Dog d = new Dog();
d.runMethod();
}
}
测试结果
六、内存地址和常量池
public class place1 {
public static void main(String[] args) {
String s1 = new String("hello");
String s2 = new String("hello");
//这里,s1 和 s2 都是通过 new 关键字创建的 String 对象,它们在堆内存中占据不同的位置。虽然它们的内容相同,但 == 比较的是它们的内存地址,因此 s1 == s2 结果为 false。
System.out.println(s1 == s2);//false
// .equals 方法比较的是内容,所以 s1.equals(s2) 结果为 true。
System.out.println(s1.equals(s2));//true
String s3="hello";
String s4="hello";
//字符串字面量(如 "hello")会被存储在常量池中。如果一个字符串已经在常量池中,后续创建相同内容的字符串字面量会引用池中的同一个对象。因此 s3 和 s4 都引用了常量池中的同一个 "hello" 字符串对象,所以 s3 == s4 结果为 true。
System.out.println(s3 == s4);//true
String s5 = new String("hello");
String s6 = "hello";
//s5 是通过 new 关键字创建的新 String 对象,它位于堆内存中,而 s6 是指向常量池中 "hello" 字符串的引用。因此,s5 和 s6 不指向同一个内存位置,所以 s5 == s6 结果为 false。
System.out.println(s5 == s6);//false
}
}
七、异常
在 Java 中,异常是在程序运行时发生的不正常情况。异常处理是一种机制,用于处理程序运行过程中可能出现的错误或异常情况,以增强程序的健壮性和容错性。
Java 中的异常可以分为两类:
检查型异常(Checked Exception):这些异常在编译时会被检查,如果方法可能抛出检查型异常,那么调用该方法的代码必须处理这个异常,要么使用 try-catch
语句捕获并处理,要么在方法声明中使用 throws
关键字声明将异常抛出给上层调用者处理。例如,IOException
、SQLException
等。
非检查型异常(Unchecked Exception):也称为运行时异常(Runtime Exception),在编译时不会被强制要求处理。这些异常通常是由于编程错误导致的,例如,NullPointerException
、ArrayIndexOutOfBoundsException
、ArithmeticException
等。
1、异常代码
此处除数为0,产生异常,虽然主函数main()也抛出了异常不处理,但运行时还是会报错
public class Demon1 {
public static void main(String[] args) throws Exception {
method();
}
public static void method() throws Exception{
int i = 1/0;
System.out.println(i);
}
}
2、try-catch解决异常
public class Demon2 {
public static void main(String[] args) {
method();
}
private static void method() {
try{
int i=1/0;
System.out.println(i);
}catch(Exception e){
//e.printStackTrace();//打印异常信息
System.out.println((1 / 1));
}
}
}
3、制造异常
public class Demon3 {
public static void main(String[] args) {
int result = 0;
try {
result = method(1,-1);
} catch (ExceptionClass e) {
throw new RuntimeException(e);
}
System.out.println(result);
}
public static int method(int x,int y) throws ExceptionClass {
// 自定义异常也叫做自己制造异常
if(y < 0){
// 使用throw关键字制造异常
throw new ExceptionClass("除数是负数了!");
}
int result = x/y;
return result;
}
}
八、API
1、StringBuffer
StringBuffer类: String和StringBuffer的区别:由于字符串是常量,因此一旦创建,其内容和长度是不可改变的。如果要对一个字符串进行修改,只能新建字符串,所以JDK中提供了一个StringBuffer类(也称为字符串缓冲区)
public class stringbufer {
public static void main(String[] args) {
System.out.println("1.add");
add();
System.out.println("2.delete");
delete();
System.out.println("3.update");
update();
}
private static void add() {
StringBuffer sb = new StringBuffer();
//追加内容
sb.append("sb");
System.out.println("append追加结果为:"+sb);
sb.insert(0,"张三");
System.out.println("append追加结果为:"+sb);
}
private static void delete() {
StringBuffer sb = new StringBuffer("12345");
//删除[1,3)的索引位置
sb.delete(1,3);
System.out.println("DeleteResult:"+sb);
//指定位置删除
sb.deleteCharAt(0);
System.out.println("DeleteResult:"+sb);
}
private static void update() {
StringBuffer sb = new StringBuffer("abcdefg");
//指定位置修改
sb.setCharAt(1,'B');
System.out.println("DeleteResult:"+sb);
//替换
sb.replace(0,2,"AB");
System.out.println("替换后的内容为:"+sb);
//字符串反转
System.out.println(sb.reverse());
}
}
2、字符串查重
编写程序,查看"abc"在字符串中出现的次数
public class OutRepeat {
public static void main(String[] args) {
String str="abcabcabckjhfoklewdhfoabc";
String key = "abc";
System.out.println(getCount(str, key));
}
/*
substring(int beginIndex, int endIndex) :
返回一个新的字符串,它是原始字符串从beginIndex(包括)开始到endIndex(不包括)的部分
*/
private static int getCount(String str, String key) {
int count = 0;
int index = str.indexOf(key);//字符串在str中首次出现的位置
while(index!=-1){
count++;
str = str.substring(index+key.length());//截取剩余字符串,然后继续查找
index = str.indexOf(key);//再次查找
}
return count;
}
}
3、System类
查看一段代码运行的时间
public class SystemObject {
public static void main(String[] args) {
long startTime = System.currentTimeMillis();
int num = 0;
for(int i = 0 ; i < 100000 ; i ++){
num+=i;
}
long endTime = System.currentTimeMillis();
System.out.println("程序运行的时间为"+(endTime-startTime));
}
}
4、数组复制
public class CopyArray {
public static void main(String[] args) {
int arr1[] = {1,2,3,4,5,6,7,8};
int arr2[] = {11,12,13,14,15,16,17,18,19};
//数组arr1从下标为2的位置拿取元素,从arr2下标为3的位置开始复制,步长为4
System.arraycopy(arr1,2,arr2,3,4);
for (int i = 0; i < arr2.length; i++) {
System.out.println(i+ "\t"+ arr2[i]);
}
}
}
5、包装类
public class PackageClass {
public static void main(String[] args) {
//字符串转化为int
String str = "6";
/*
Integer.parseInt(str) 是 Java 中的一个方法,用于将一个表示整数的字符串 str 转换为对应的 int 类型的整数。
如果字符串 str 不能被正确解析为整数(例如,字符串不包含有效的整数字符,或者格式不正确),将会抛出 NumberFormatException 异常。
*/
int num = Integer.parseInt(str);
System.out.println(num==6);
//int类型转化为字符串
int num2 = 10;
Integer integer1 = new Integer(num2);// 创建一个 Integer 对象 integer1,并通过构造函数将 num2 的值传递进去进行初始化
System.out.println(integer1.toString()+1); // 调用 integer1 的 toString 方法将其转换为字符串,然后与数字 1 进行字符串拼接并输出
int num3 = 30;
Integer integer2 = new Integer(20);
int sum = integer2.intValue()+num3;//将 Integer 对象转换为基本数据类型 int
System.out.println(sum);
}
}
九、集合
集合按照其存储结构分为两大类:单列集合 双列集合
Collection:单列集合类的根接口,用于存储一系列符合某种规则的元素,它有两个重要的子接口,分别是List和Set 其中
List的特点:元素有序,可重复。
Set的特点是元素无序,不可重复
List接口的主要实现类,ArrayList和LinkedList Set接口的主要实现类由HashSet和TreeSet Map:双列集合的根接口,用于存储具有键(key)、值(value)映射关系的元素,每个元素都包含一对键值,在使用Map集合时可以通过指定的key找到对应的Value
1、ArryList
Java 集合框架提供了一组用于存储和操作一组对象的数据结构。主要的集合接口包括 Collection 和 Map ArrayList内部封装了一个长度可变的数组对象,当存入元素超过数组长度时,ArrayList会在内存中分配一个跟大的数组来存储这些元素。所以可以将ArrayList集合类当作可变数组来看; 优点: 数组结构允许程序通过索引的方式来访问元素,因此使用ArryList集合查找元素很便捷 缺点: 由于ArrayList集合的底层是使用一个数组来保存元素,在增加或者删除指定位置的元素会导致创建新的数组 效率比较低,因此不适合做大量的增删改查操作;
public class SignalGather {
public static void main(String[] args) {
ArrayList list = new ArrayList<>();
list.add("你好");
list.add("我好");
list.add("大家好");
System.out.println("List Length:" + list.size());
System.out.println("Value Index 2:" + list.get(2));
/*Iterator(迭代器)是一个用于遍历集合元素的接口。
它提供了一种统一的方式来遍历不同类型的集合,而无需关心集合的具体实现细节。
*/
//迭代遍历
Iterator iterator = list.iterator();//获取了一个列表list的迭代器iterator
while (iterator.hasNext()) {//检查迭代器中是否还有下一个元素
Object obj = iterator.next();//iterator.next() 方法返回迭代器中的下一个元素,并将其赋值给 obj 变量
System.out.println(obj);
}
//增强for循环
for (Object o : list) {
System.out.println(o);
}
//字母排序
ArrayList<String> sites = new ArrayList<String>();
sites.add("Taobao");
sites.add("Wiki");
sites.add("Runoob");
sites.add("Weibo");
sites.add("Google");
Collections.sort(sites); // 字母排序
for (String i : sites) {
System.out.println(i);
}
}
}
2、LinkedList
public class Link {
public static void main(String[] args) {
LinkedList list = new LinkedList();
list.add("Link1");
list.add("Link2");
list.add("Link3");
list.add("Link4");
list.add("Link5");
System.out.println(list);
//第一个位置添加数据
list.addFirst("司马懿");
//第三个位置添加数据
list.add(3,"link3");
System.out.println(list);
//获取第一个位置上的数据
System.out.println(list.getFirst());
//删除第三个位置的数据
list.remove(3);
System.out.println(list);
//删除第一个位置的数据
list.removeFirst();
System.out.println(list);
}
}
3、Set
Set接口和List接口一样,都继承自Collection接口 特点:无序,不可重复 HashSet集合 HashSet是Set集合的实现类,它所存储的数据是不可重复的,元素都是无序的 判断是否重复的底层原理:当向HashSet集合中添加一个对象时,首先会调用该对象的hashCode()方法来计算对象的哈希值,从而确定元素的位置如果此时哈希值相同,在调用对象的equals()方法来确保该位置没有重复元素。
Student类
public class student {
public int getId() {
return id;
}
public void setId(int id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String toString() {
return "student{" +
"id=" + id +
", name='" + name + '\'' +
'}';
}
private int id;
private String name;
public student(int id, String name) {
this.id = id;
this.name = name;
}
public student(){
}
}
测试类
public class Demon {
public static void main(String[] args) {
HashSet hashSet = new HashSet();
student s1 = new student(1, "张三");
student s2 = new student(2, "李四");
student s3 = new student(3, "王五");
hashSet.add(s1);
hashSet.add(s2);
hashSet.add(s3);
System.out.println(hashSet);
}
}
4、Map接口
Map接口: Map接口是一个双列集合,它的每一个元素都包含一个键对象key和值对象value,键和值对象之间存在一种对应关系, 称为映射 从Map集合种访问元素时,只要指定了key,就能找到对应的value。
Map接口还有一个实现类Hashtable,它和HashMap的区别在于它是线程安全的
import java.util.*;
public class MapClass {
public static void main(String[] args) {
Map map = new HashMap();
map.put("1","关羽");
map.put("2","张飞");
map.put("3","赵云");
map.put("2","张飞");
map.put("4","马超");
map.put("5","黄忠");
map.put(6,"魏延");
map.put(7,"周仓");
System.out.println(map.get("2"));
System.out.println(map.get(6));
System.out.println("第一种遍历方式:");
Set set = map.keySet();//获取map中的所有键值对,存储在一个Set中
Iterator iterator = set.iterator();//获取Set的迭代器
while(iterator.hasNext()){
Object obj = iterator.next();//获取键
Object value = map.get(obj);//获取键值
System.out.println(obj + ":" + value);
}
System.out.println("第二种遍历方式:");
Set sett = map.entrySet();
Iterator iterator1 = sett.iterator();
while(iterator1.hasNext()){
Map.Entry entry = (Map.Entry) iterator1.next();//获取下个键值对
Object key = entry.getKey();
Object value = entry.getValue();
System.out.println(key + ":" + value);
}
System.out.println("第三种遍历方式:");
Collection values = map.values();//获取map中的所有值,存储在Collection中
Iterator iterator3 = values.iterator();
while(iterator3.hasNext()){
Object obj = iterator3.next();
System.out.println(obj);
}
}
}
5、泛型
Java 泛型(generics)是 JDK 5 中引入的一个新特性, 泛型提供了编译时类型安全检测机制,该机制允许程序员在编译时检测到非法的类型。
泛型的本质是参数化类型,也就是说所操作的数据类型被指定为一个参数。
java 中泛型标记符:
- E - Element (在集合中使用,因为集合中存放的是元素)
- T - Type(Java 类)
- K - Key(键)
- V - Value(值)
- N - Number(数值类型)
- ? - 表示不确定的 java 类型
1.打印不同数组元素
public class GenericMethodTest
{
// 泛型方法 printArray
public static < E > void printArray( E[] inputArray )
{
// 输出数组元素
for ( E element : inputArray ){
System.out.printf( "%s ", element );
}
System.out.println();
}
public static void main( String args[] )
{
// 创建不同类型数组: Integer, Double 和 Character
Integer[] intArray = { 1, 2, 3, 4, 5 };
Double[] doubleArray = { 1.1, 2.2, 3.3, 4.4 };
Character[] charArray = { 'H', 'E', 'L', 'L', 'O' };
System.out.println( "整型数组元素为:" );
printArray( intArray ); // 传递一个整型数组
System.out.println( "\n双精度型数组元素为:" );
printArray( doubleArray ); // 传递一个双精度型数组
System.out.println( "\n字符型数组元素为:" );
printArray( charArray ); // 传递一个字符型数组
}
}
2. 得到不同类型的最大值
public class MaximumTest
{
// 比较三个值并返回最大值
public static <T extends Comparable<T>> T maximum(T x, T y, T z)
{
T max = x; // 假设x是初始最大值
if ( y.compareTo( max ) > 0 ){
max = y; //y 更大
}
if ( z.compareTo( max ) > 0 ){
max = z; // 现在 z 更大
}
return max; // 返回最大对象
}
public static void main( String args[] )
{
System.out.printf( "%d, %d 和 %d 中最大的数为 %d\n\n",
3, 4, 5, maximum( 3, 4, 5 ) );
System.out.printf( "%.1f, %.1f 和 %.1f 中最大的数为 %.1f\n\n",
6.6, 8.8, 7.7, maximum( 6.6, 8.8, 7.7 ) );
System.out.printf( "%s, %s 和 %s 中最大的数为 %s\n","pear",
"apple", "orange", maximum( "pear", "apple", "orange" ) );
}
}
3.类型通配符
import java.util.*;
public class GenericTest {
public static void main(String[] args) {
List<String> name = new ArrayList<String>();
List<Integer> age = new ArrayList<Integer>();
List<Number> number = new ArrayList<Number>();
name.add("icon");
age.add(18);
number.add(314);
getData(name);
getData(age);
getData(number);
}
public static void getData(List<?> data) {
System.out.println("data :" + data.get(0));
}
}
十、线程基础
Java 的线程是实现并发编程的核心工具,允许同时执行多个任务。线程在 Java 中的引入主要是为了提高程序的效率和响应性。以下是关于 Java 线程的详细介绍:
1、 线程的基本概念
线程(Thread):线程是程序中独立执行的最小单位,每个线程都有自己的程序计数器、栈和局部变量。多个线程共享进程的资源(如内存)。
进程(Process):进程是系统进行资源分配和调度的基本单位,是线程的集合。一个进程可以包含多个线程。
2.、创建线程的方法
Java 提供了两种主要的方式来创建线程:
2.1 继承 Thread 类
MyThread类
public class MyThread extends Thread{
public void run(){
while (true){
System.out.println("myThread已启动");
}
}
}
测试类
public class Demon {
public static void main(String[] args) {
MyThread myThread = new MyThread();
myThread.start();
run();
}
private static void run(){
while (true){
System.out.println("主函数已启动");
}
}
}
2.2 实现 Runnable 接口
MyThread2类
public class MyThread2 implements Runnable{
public void run(){
while(true){
System.out.println("MyThread2正在运行");
}
}
}
测试类
public class Demon2 {
public static void main(String[] args) {
MyThread2 myThread2 = new MyThread2();
Thread thread = new Thread( myThread2);
thread.start();
while (true){
System.out.println("Main2正在运行");
}
}
}
3、 线程的生命周期
新建(New):线程对象被创建,但尚未调用 start() 方法。
可运行(Runnable):线程已经调用了 start() 方法,但尚未获得 CPU 时间片。
运行(Running):线程获得了 CPU 时间片并在执行 run() 方法。
阻塞(Blocked):线程因等待资源或锁而被阻塞。
等待(Waiting):线程因调用 wait() 方法而进入等待状态,直到其他线程调用 notify() 或 notifyAll()。
超时等待(Timed Waiting):线程在指定时间内等待,如调用 sleep() 或 join() 方法。
终止(Terminated):线程执行完毕或者因异常退出。
4、线程的控制方法
start():启动线程,调用 run() 方法。
run():线程的主方法,包含线程要执行的代码。
sleep(long millis):使当前线程暂停执行指定的时间。
join():等待线程终止。
yield():使当前线程让出 CPU 资源。
interrupt():中断线程的休眠、等待或阻塞状态。
isAlive():检查线程是否还在运行。
5、线程优先级
在此代码中,虽然设置了最高和最低的优先级,但是是否运行得看概率,概率大的优先级高,但不一定先运行
class Task implements Runnable{
@Override
public void run() {
for (int i = 0; i < 10; i++) {
try {
Thread.sleep(500);
}catch (InterruptedException e){
e.printStackTrace();
}if(i>0){
System.out.println(Thread.currentThread().getName() + i);
}
}
}
}
public class Priority {
public static void main(String[] args) {
Task t1 = new Task();
Task t2 = new Task();
Thread p1 = new Thread(t1,"最高优先级");
Thread p2 = new Thread(t2,"最低优先级");
p1.setPriority(Thread.MAX_PRIORITY); // 设置最高优先级
p2.setPriority(Thread.NORM_PRIORITY); // 设置普通优先级
p1.start();
p2.start();
}
}
6、线程让步
线程让步可以通过 yield ()方法来实现,该方法和 sleep ()方法有点相似,都 可以让当前正在运行的线程暂停,区别在于 yield() 方法不会阻塞该线程,它只是将线程转换成就绪状态,让系统的调度器重新调度一次。 当某个线程调用 yield ()方法之后,只有与当前线程优先级相同或者更高的线程才能获得执行的机会。
class YieldThread extends Thread{
public YieldThread(String name){
super(name);
}
@Override
public void run() {
for (int i = 0; i < 6; i++) {
System.out.println(Thread.currentThread().getName() + "---" + i);
if(i==3){
//Thread.yield();
System.out.println("线程让步");
Thread.yield();
}
}
}
}
public class Demon {
public static void main(String[] args) {
Thread t1= new YieldThread("线程A");
Thread t2 = new YieldThread("线程B");
t1.start();
t2.start();
}
}
7、线程插队
class EcyThread implements Runnable{
@Override
public void run() {
for (int i = 0; i < 8; i++) {
System.out.println(Thread.currentThread().getName() + "--" + i);
try{
Thread.sleep(1000);
}catch (Exception e){
e.printStackTrace();
}
}
}
}
public class CutThread {
public static void main(String[] args) throws InterruptedException{
EcyThread ecyThread = new EcyThread();
Thread thread1 = new Thread(ecyThread,"线程1");
thread1.start();
for (int i = 0; i < 8; i++) {
System.out.println(Thread.currentThread().getName() + "--" + i);
if(i==4){
System.out.println("线程插队");
thread1.join();
}
Thread.sleep(1000);
}
}
}
8、Sleep
class Task2 implements Runnable{
@Override
public void run() {
for (int i = 0; i < 10; i++) {
try {
System.out.println(Thread.currentThread().getName()+i);
Thread.sleep(1000);
} catch (InterruptedException e) {
throw new RuntimeException(e);
}
}
}
}
public class sleep {
public static void main(String[] args) {
Task2 task2 = new Task2();
Thread thread = new Thread(task2,"线程");
thread.start();
}
}
9、售票案例
现有10张票,现在通过4个窗口进行售卖。
第一种方式,直接继承Thread的四个窗口进行票的减减售卖,此时发现每个窗口都卖了10张票,所以代码存在问题
class TickThread extends Thread{
public void run(){
int count = 0;
int i = 10;
while(true){
if(i>0){
Thread th = Thread.currentThread();
String name = th.getName();
System.out.println(name+"正在售出第" + (i--) + "张票\t\t"+count++);
}
}
}
}
public class Demon3 {
public static void main(String[] args) {
TickThread tickThread = new TickThread();
new Thread(tickThread,"窗口1").start();
new Thread(tickThread,"窗口2").start();
new Thread(tickThread,"窗口3").start();
new Thread(tickThread,"窗口4").start();
}
}
第二种方式,继承Runnable的多线程来售票 ,现在发现售票正常
class TickThread2 implements Runnable{
private int i=10;
public void run() {
while (true){
if(i>0){
Thread th = Thread.currentThread();
String name = th.getName();
System.out.println(name+"正在发售第" +(i--) + "张票");
}
}
}
}
public class Demno4 {
public static void main(String[] args) {
TickThread2 tickThread2 = new TickThread2();
new Thread(tickThread2,"窗口1").start();
new Thread(tickThread2,"窗口2").start();
new Thread(tickThread2,"窗口3").start();
new Thread(tickThread2,"窗口4").start();
}
}
第三种方式来改进一下,使用 synchronized 关键字对 obj 对象进行加锁,然后抛出异常来每隔半秒售票,查看售票结果正常
class TicketThread implements Runnable{
private int i = 10;
Object obj = new Object();
@Override
public void run() {
while (i>0){
synchronized (obj){
//使用 synchronized 关键字对 obj 对象进行加锁,这意味着同一时刻只有一个线程能够进入这个同步块执行代码。
try{
Thread.sleep(500);
}catch (InterruptedException e){
throw new RuntimeException(e);
}
if(i>0){
System.out.println(Thread.currentThread().getName() + "卖出的票" + i--);
}
}
}
}
}
public class Ticket {
public static void main(String[] args) {
TicketThread ticketThread = new TicketThread();
new Thread(ticketThread,"窗口1").start();
new Thread(ticketThread,"窗口2").start();
new Thread(ticketThread,"窗口3").start();
new Thread(ticketThread,"窗口4").start();
}
}
第四种方式来进行售票,创建一个synchronized修饰的售票函数,然后售完退出,结果正常
import java.util.concurrent.atomic.AtomicInteger;
class TickThread2 implements Runnable{
private AtomicInteger i = new AtomicInteger(10);
@Override
public void run() {
while(i.get()>0){
method();
}
}
private synchronized void method() {
try{
Thread.sleep(500);
}catch(InterruptedException e){
e.printStackTrace();
}if (i.get()>0){
System.out.println(Thread.currentThread().getName() + "卖出的票" + i.decrementAndGet());
}
else {
System.exit(0);
}
}
}
public class Ticket2 {
public static void main(String[] args) {
TickThread2 tickThread2 = new TickThread2();
new Thread(tickThread2,"窗口1").start();
new Thread(tickThread2,"窗口2").start();
new Thread(tickThread2,"窗口3").start();
new Thread(tickThread2,"窗口4").start();
}
}
十一、字节流
1、InputStream
public class InputStream {
public static void main(String[] args) throws Exception {
FileInputStream fileInputStream = new FileInputStream("C:/Users/20665/OneDrive/桌面/1.txt");
int b=0;
while(true){
b=fileInputStream.read();
if(b==-1){
break;
}
System.out.println(b);
}
fileInputStream.close();
}
}
2、OutputStream
public class OutputStreamm {
public static void main(String[] args) throws Exception{
OutputStream outputStream = new FileOutputStream("C:/Users/20665/OneDrive/桌面/1.txt");
String str = "张三";
byte[] bytes = str.getBytes();
for (int i = 0; i < bytes.length; i++) {
outputStream.write(bytes[i]);
}
outputStream.close();
}
}
3、Copy
把桌面上的1.txt文件内容复制到新建的一个2.txt文件中
package com.jn.io;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.ObjectOutputStream;
import java.io.OutputStream;
public class copy {
//throws Exception 表示该方法可能抛出异常,需要在调用处进行处理或继续抛出
public static void main(String[] args) throws Exception{
//创建了一个文件输入流,用于读取指定路径的文件
InputStream is = new FileInputStream("C:/Users/20665/OneDrive/桌面/1.txt");
// 创建了一个文件输出流,用于将数据写入指定路径的文件
OutputStream os = new FileOutputStream("C:/Users/20665/OneDrive/桌面/2.txt");
int len;
long beginfTime = System.currentTimeMillis();
//循环读取文件的内容,并将读取到的字节写入到输出文件
while ((len = is.read())!=-1){//当 read() 方法读取到文件末尾时,会返回 -1
os.write(len);
}
long endTime = System.currentTimeMillis();
System.out.println("cope time:"+(endTime - beginfTime));
is.close();
os.close();
}
}
4、Buffer 缓冲区
把复制文件存入到缓冲区来减少系统操作时间
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.OutputStream;
public class Buffer {
//抛出异常
public static void main(String[] args) throws Exception{
//创建输入流
InputStream is = new FileInputStream("C:/Users/20665/OneDrive/桌面/java/io/1.txt");
//输出流
OutputStream os = new FileOutputStream("C:/Users/20665/OneDrive/桌面/java/io/buffer/2.txt");
//创建一个大小为 1024 字节的缓冲区,用于在读取和写入文件时提高效率。
byte[] buffer = new byte[1024];
int len;
long beginTime = System.currentTimeMillis();
//从输入流中读取数据到缓冲区,并获取读取的字节数。 将缓冲区中有效数据(从索引 0 开始,长度为 len )写入输出流
while((len = is.read(buffer))!=-1) os.write(buffer, 0, len);
long endTime = System.currentTimeMillis();
System.out.println("拷贝时间为:" + (endTime - beginTime));
is.close();
os.close();
}
}
5、Buffer缓冲流
之间创建一个带缓冲区的输入输出流来节省系统响应时间
public class BufferFlow {
public static void main(String[] args) throws Exception{
//创建一个带缓冲区的输入流
BufferedInputStream bis = new BufferedInputStream(new FileInputStream("C:/Users/20665/OneDrive/桌面/java/io/1.txt"));
//输出流
BufferedOutputStream bos = new BufferedOutputStream(new FileOutputStream("C:/Users/20665/OneDrive/桌面/java/io/buffer/2.txt"));
int len;
while((len = bis.read())!= -1){
bos.write(len);
}
bis.close();
bos.close();
}
}