首页
复制
搜索
前进
后退
重载网页
和我当邻居
给我留言吧
首页
壁纸
影院
统计
关于
友链
留言
Search
1
面向对象核心
28 阅读
2
Java枚举类
26 阅读
3
Lambda表达式与Stream
23 阅读
4
Java常用API与类
20 阅读
5
集合框架主要集合类及其用法
20 阅读
随笔
JAVA
Mysql
JavaWeb
Tools
登录
Search
标签搜索
集合框架
fnm
枚举
数组
基础语法
面向对象
Comparator
Comparable
Cloneable
内部类
普通成员内部类
普通局部内部类
静态成员内部类
静态成员代码块
Throwable
异常类
密封类
代码块
普通成员代码块
Mathlei
阿简
累计撰写
34
篇文章
累计收到
0
条评论
首页
栏目
随笔
JAVA
Mysql
JavaWeb
Tools
页面
壁纸
影院
统计
关于
友链
留言
搜索到
34
篇与
的结果
2024-08-09
Lambda表达式与Stream
什么是 Lambda 表达式?Lambda 表达式是 Java 8 引入的一种新特性,它是一种简洁的表示能够传递的代码块。Lambda 表达式可以表示一个函数接口的实现。函数接口是只包含一个抽象方法的接口,例如 Runnable、Comparator 或 Java 8 引入的 java.util.function 包中的接口。Lambda 表达式的语法(parameters) -> expression 或 (parameters) -> { statements; }示例// 不带参数的Lambda表达式 Runnable r = () -> System.out.println("Hello Lambda!"); // 带一个参数的Lambda表达式 Consumer<String> consumer = (s) -> System.out.println(s); // 带有两个参数和返回值的Lambda表达式 Comparator<Integer> comparator = (a, b) -> Integer.compare(a, b);使用 Lambda 表达式的场景简化匿名内部类的使用: Lambda 表达式提供了一种简化写法,替代冗长的匿名内部类。配合函数式接口: 可以与 java.util.function 包中的函数式接口配合使用,例如 Predicate、Function、Consumer、Supplier、UnaryOperator 等。Java Stream API什么是 Stream?Stream 是 Java 8 中引入的一种新特性,它是一种用来处理集合数据的高级抽象。Stream 可以进行非常复杂的数据操作,例如过滤、排序、映射、归约等,能够更简洁、方便地进行集合操作。Stream 的使用步骤创建 Stream: 可以通过集合、数组、文件等多种方式创建一个 Stream。中间操作: 对 Stream 进行一系列转换(如 filter、map、sorted 等),这些操作是懒执行的。终端操作: 对 Stream 进行最终操作(如 forEach、collect、reduce 等),触发所有中间操作的执行。示例代码List<String> list = Arrays.asList("java", "c", "python", "c++", "VB", "C#"); // 使用Stream进行过滤和遍历 list.stream() .filter(s -> s.length() > 2) .forEach(System.out::println);常用的 Stream 操作filter(Predicate): 对元素进行过滤。map(Function): 将每个元素转换为另一种类型。sorted(Comparator): 对元素进行排序。collect(Collector): 将 Stream 转换为集合、数组等。reduce(BinaryOperator): 归约操作,将所有元素根据给定的规则合并为一个结果。结合使用 Lambda 表达式与 StreamLambda 表达式通常与 Stream API 一起使用,可以让代码更简洁、易读。例如:List<String> names = Arrays.asList("Alice", "Bob", "Charlie", "David"); // 使用Stream API和Lambda表达式筛选和转换数据 List<String> result = names.stream() .filter(name -> name.startsWith("A")) .map(String::toUpperCase) .collect(Collectors.toList()); System.out.println(result); // 输出: [ALICE]常见的函数式接口Predicate<T>: 接收一个参数,返回一个布尔值结果。Predicate<String> isLongerThan5 = s -> s.length() > 5;Consumer<T>: 接收一个参数,无返回值。Consumer<String> print = s -> System.out.println(s);Function<T, R>: 接收一个参数,返回一个结果。Function<String, Integer> length = s -> s.length();Supplier<T>: 无参数,返回一个结果。Supplier<String> supplier = () -> "Hello";UnaryOperator<T>: 接收一个参数,返回一个相同类型的结果。UnaryOperator<String> toUpperCase = s -> s.toUpperCase();
2024年08月09日
23 阅读
0 评论
0 点赞
2024-08-09
泛型相关
泛型的基本操作泛型定义泛型即“参数化类型”,就是将具体的类型变成参数化类型。在声明一个泛型时,传递的是一个类型形参(Type Parameter),在调用时传递的是一个类型实参(Type Argument)。 参考形式参数泛型的好处示例代码:JavaBean:圆类型class Circle{ private double radius; public Circle(double radius) { super(); this.radius = radius; } public double getRadius() { return radius; } public void setRadius(double radius) { this.radius = radius; } @Override public String toString() { return "Circle [radius=" + radius + "]"; } }比较器import java.util.Comparator; public class CircleComparator implements Comparator{ @Override public int compare(Object o1, Object o2) { //强制类型转换 Circle c1 = (Circle) o1; Circle c2 = (Circle) o2; return Double.compare(c1.getRadius(), c2.getRadius()); } }测试类public class TestGeneric { public static void main(String[] args) { CircleComparator com = new CircleComparator(); System.out.println(com.compare(new Circle(1), new Circle(2))); System.out.println(com.compare("圆1", "圆2"));//运行时异常:ClassCastException } }那么我们在使用如上面这样的接口时,如果没有泛型或不指定泛型,很麻烦,而且有安全隐患。因为在设计(编译)Comparator接口时,不知道它会用于哪种类型的对象比较,因此只能将compare方法的形参设计为Object类型,而实际在compare方法中需要向下转型为Circle,才能调用Circle类的getRadius()获取半径值进行比较。使用泛型:比较器:class CircleComparator implements Comparator<Circle>{ @Override public int compare(Circle o1, Circle o2) { //不再需要强制类型转换,代码更简洁 return Double.compare(o1.getRadius(), o2.getRadius()); } }测试类import java.util.Comparator; public class TestGeneric { public static void main(String[] args) { CircleComparator com = new CircleComparator(); System.out.println(com.compare(new Circle(1), new Circle(2))); // System.out.println(com.compare("圆1", "圆2"));//编译错误,因为"圆1", "圆2"不是Circle类型,是String类型,编译器提前报错,而不是冒着风险在运行时再报错 } }如果有了泛型并使用泛型,那么既能保证安全,又能简化代码。因为把不安全的因素在编译期间就排除了;既然通过了编译,那么类型一定是符合要求的,就避免了类型转换。声明类型变量\<T>声明类或接口时,在类名或接口名后面声明类型变量,这样的类或接口称为泛型类或泛型接口【修饰符】 class 类名<类型变量列表> 【extends 父类】 【implements 父接口们】{ } 【修饰符】 interface 接口名<类型变量列表> 【implements 父接口们】{ } 例如: public class ArrayList<E> public interface Map<K,V>{ .... } 声明方法时,在【修饰符】与返回值类型之间声明类型变量,我们把声明(是声明不是单纯的使用)了类型变量的方法称为泛型方法【修饰符】 <类型变量列表> 返回值类型 方法名(【形参列表】)【throws 异常列表】{ //... } 例如:java.util.Arrays类中的 public static <T> List<T> asList(T... a){ .... }自定义泛型结构自定义泛型类和泛型接口在声明类或接口时,类或接口中定义某个成员时,该成员有些类型是不确定的,而这个类型需要在使用这个类或接口时才确定,可以使用泛型。声明泛型类语法格式:【修饰符】 class 类名<类型变量列表> { }注意:<类型变量列表>:可以是一个或多个类型变量,一般都是使用单个的大写字母表示。例如:<T>、<K,V> 等。当类或接口上声明了<类型变量列表>时,其中的类型变量不能用于静态成员上。示例: public class GerClass<T> { private T obj;//成员变量使用类上定义的类型变量T public T getObj() {//实例方法使用类上定义的类型变量T return obj; } public void setObj(T obj) { this.obj = obj; } //public static void test(T t){ } //此时类型变量T不能用在静态成员上 }声明泛型接口语法格式:【修饰符】 interface 接口名<类型变量列表> 【implements 父接口们】{ }示例://泛型接口 public interface GerInterface<T> { void show(T t); }泛型类和接口的子类或实现类泛型类和接口一样可以被继承或实现,一个类在继承父类或实现接口时分两种情况:子类或实现类明确泛型类的类型参数变量//定义实现类时,明确接口中声明的类型参数,此时实现类不再是泛型类 public class GerInterfaceImpl implements GerInterface<String> { @Override public void show(String t) { System.out.println(t); } }public class User implements Comparable<User>{ @Override public int compareTo(User u){ return 0; } }子类不明确泛型类的类型参数变量//定义实现类时,实现类不明确接口中声明的类型参数,实现类仍然是泛型类 public class GerInterfaceImpl<T> implements GerInterface<T> { @Override public void show(T t) { System.out.println(t); } }//ArrayList类实现了泛型接口,未明确泛型类型参数,依然是泛型类 public class ArrayList<E> extends AbstractList<E> implements List<E>, RandomAccess, Cloneable, java.io.Serializable { }使用泛型类和接口在使用这种参数化的类与接口创建对象时,我们需要指定泛型变量的实际类型参数(必须是引用数据类型) public static void main(String[] args) { //使用泛型类或接口时,明确泛型参数类型为String GerInterface<String> gi = new GerInterfaceImpl<String>(); //gi.show(123);//泛型确定了String,这里编译失败 gi.show("hello"); }指定泛型实参时,必须左右两边类型参数一致。JDK1.7后支持简写形式,右边类型参数可以省略:GerInterface<String> gi = new GerInterfaceImpl<>();//省略右边泛型类型当使用参数化类型的类或接口时,如果没有指定泛型,相当于Object类型。//实现类确定了泛型类型 class Circle implements Comparable<Circle>{ private double radius; public Circle(double radius) { super(); this.radius = radius; } public double getRadius() { return radius; } public void setRadius(double radius) { this.radius = radius; } @Override public String toString() { return "Circle [radius=" + radius + "]"; } @Override public int compareTo(Circle c){//参数类型确定 return Double.compare(radius,c.radius); } } //类型擦除: public class CircleComparator implements Comparator{ @Override public int compare(Object o1, Object o2) { //未指定泛型类型,默认为Object,使用时还要强制类型转换 Circle c1 = (Circle) o1; Circle c2 = (Circle) o2; return Double.compare(c1.getRadius(), c2.getRadius()); } }使用泛型类:public class TestGeneric{ public static void main(String[] args) { //语文老师使用时: Student<String> stu1 = new Student<String>("张三", "良好"); //数学老师使用时: //Student<double> stu2 = new Student<double>("张三", 90.5);//错误,必须是引用数据类型 Student<Double> stu2 = new Student<Double>("张三", 90.5); //英语老师使用时: Student<Character> stu3 = new Student<Character>("张三", 'C'); //错误的指定 //Student<Object> stu = new Student<String>();//错误的 } }继承泛型类并指定类型变量:class ChineseStudent extends Student<String>{//继承时确定了泛型类型 public ChineseStudent() { super(); } public ChineseStudent(String name, String score) { super(name, score); } }使用泛型类的子类:public class TestGeneric{ public static void main(String[] args) { //语文老师使用时: ChineseStudent stu = new ChineseStudent("张三", "良好"); } } 自定义泛型方法前面介绍了在定义类、接口时可以声明<类型变量>,在该类的方法和属性定义、接口的方法定义中,这些<类型变量>可被当成普通类型来用。使用泛型时,如果外界只关心某个方法,而不关心类其他的成员,那么可以只在该方法上声明泛型,方法泛型化,称为泛型方法。语法格式:【修饰符】 <类型变量列表> 返回值类型 方法名(【形参列表】)【throws 异常列表】{ //... }<类型变量列表>:可以是一个或多个类型变量,一般都是使用单个的大写字母表示。例如:、<K,V>等。静态方法也可以单独泛型化。区别泛型类或接口中的静态方法(不能使用泛型类或接口定义的泛型变量)。示例:public class GernericMethod { //泛型方法 public static <T> T getMsg(T t){ return t; } }public static void main(String[] args) { GernericMethod.getMsg("hello"); }类型变量的上限与泛型擦除类型变量的上限当在声明类型变量时,如果不希望这个类型变量代表任意引用数据类型,而是某个系列的引用数据类型,那么可以设定类型变量的上限。语法格式:<类型变量 extends 上限>如果有多个上限<类型变量 extends 上限1 & 上限2>如果多个上限中有类有接口,那么只能有一个类,而且必须写在最左边。接口的话,可以多个。如果在声明<类型变量>时没有指定任何上限,默认上限是java.lang.Object。class A implements Comparable { @Override public int compareTo(Object o) { return 0; } } public class MethodTest2 { //要求传入的值是Number和Comparable的孩子 static <T extends Number & Comparable> void cc(T t) { } //要求传入的值是A的孩子 static <T extends A> void mm(T t){ } //要求传入的值是可以比较的 public static <T extends Comparable> void show(T t) { } @Test public void test02() { Double d; cc(3.14); cc(100); // cc("张三"); A a = new A(); mm(a); } @Test public void test01() { A a = new A(); show(a); Integer i; show(6666); show(3.14); show("张三"); } }泛型擦除1没有指定泛型当使用参数化类型的类或接口时,会发生泛型擦除,自动按照最左边的第一个上限处理。如果没有指定上限,上限即为Object。package com.atguigu.limmit; import java.util.ArrayList; import java.util.Collection; public class TestErase { public static void main(String[] args) { NumberTools tools = new NumberTools(8,5); Number sum = tools.getSum();//自动按照Number处理 System.out.println("sum = " + sum); Number subtract = tools.getSubtract(); System.out.println("subtract = " + subtract); Collection coll = new ArrayList(); coll.add("hello"); coll.add(1); for (Object o : coll) {//自动按照Object处理 System.out.println(o); } } } 泛型存在于编译时类型通配符当我们声明一个变量/形参时,这个变量/形参的类型是一个泛型类或泛型接口,例如:Comparator类型,但是我们仍然无法确定这个泛型类或泛型接口的类型变量<T>的具体类型,此时我们考虑使用类型通配符。例如:这个学生类是一个参数化的泛型类,代码如下public class Student<T>{ private String name; private T score; public Student() { super(); } public Student(String name, T score) { super(); this.name = name; this.score = score; } public String getName() { return name; } public void setName(String name) { this.name = name; } public T getScore() { return score; } public void setScore(T score) { this.score = score; } @Override public String toString() { return "姓名:" + name + ", 成绩:" + score; } }<?>任意类型例如:我们要声明一个学生数组,可以存贮任意泛型的学生。测试类public class TestGeneric { public static void main(String[] args) { // 语文老师使用时: Student<String> stu1 = new Student<String>("张三", "良好"); // 数学老师使用时: // Student<double> stu2 = new Student<double>("张三", 90.5);//错误,必须是引用数据类型 Student<Double> stu2 = new Student<Double>("张三", 90.5); // 英语老师使用时: Student<Character> stu3 = new Student<Character>("张三", 'C'); Student<Object> stu4 = new Student<>("王五", new Object()); Student<Object>[] arr0 = new Student[4];//只能添加stu4 Student<?>[] arr = new Student[4]; arr[0] = stu1; arr[1] = stu2; arr[2] = stu3; arr[3] = stu4; } }<? extends 上限>用于设定通配符上限例如:我们要声明一个学生管理类,这个管理类要包含一个方法,找出学生数组中成绩最高的学生对象。要求学生的成绩的类型必须可比较大小,实现Comparable接口。学生管理类:class StudentService { //分数score的类型必须是实现了Comparable接口的 public static Student max(Student<? extends Comparable>[] arr){ Student<? extends Comparable> max = arr[0]; for (int i = 0; i < arr.length; i++) { if(arr[i].getScore().compareTo(max.getScore())>0){ max = arr[i]; } } return max; } }测试类public class TestGeneric { public static void main(String[] args) { Student<? extends Double>[] arr = new Student[3]; arr[0] = new Student<Double>("张三", 90.5); arr[1] = new Student<Double>("李四", 80.5); arr[2] = new Student<Double>("王五", 94.5); Student<? extends Comparable> max = StudentService.max(arr); System.out.println(max); } }<? super 下限>用于设定通配符下限现在要声明一个数组工具类,包含可以给任意对象数组进行从小到大排序,只要你指定定制比较器对象,而且这个定制比较器对象可以是当前数组元素类型自己或其父类的定制比较器对象数组工具类:class MyArrays{ public static <T> void sort(T[] arr, Comparator<? super T> c){ for (int i = 1; i < arr.length; i++) { for (int j = 0; j < arr.length-i; j++) { if(c.compare(arr[j], arr[j+1])>0){ T temp = arr[j]; arr[j] = arr[j+1]; arr[j+1] = temp; } } } } }例如:有如下JavaBeanclass Person{ private String name; private int age; public Person(String name, int age) { super(); this.name = name; this.age = age; } public Person() { super(); } 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 String toString() { return "name=" + name + ", age=" + age; } } class Student extends Person{ private int score; public Student(String name, int age, int score) { super(name, age); this.score = score; } public Student() { super(); } public int getScore() { return score; } public void setScore(int score) { this.score = score; } @Override public String toString() { return super.toString() + ",score=" + score; } }测试类public class TestGeneric { public static void main(String[] args) { Student[] all = new Student[3]; all[0] = new Student("张三", 23, 89); all[1] = new Student("李四", 22, 99); all[2] = new Student("王五", 25, 67); MyArrays.sort(all, new Comparator<Person>() { @Override public int compare(Person o1, Person o2) { return o1.getAge() - o2.getAge(); } }); System.out.println(Arrays.toString(all)); MyArrays.sort(all, new Comparator<Student>() { @Override public int compare(Student o1, Student o2) { return o1.getScore() - o2.getScore(); } }); System.out.println(Arrays.toString(all)); } }
2024年08月09日
6 阅读
0 评论
0 点赞
2024-08-09
集合框架主要集合类及其用法
Java集合框架Java集合框架是一组在java.util包中定义的类和接口,用于管理和存储数据集合。ListList是一个有序集合,可以包含重复的元素。常用实现ArrayList:基于动态数组,提供快速随机访问。LinkedList:基于链表,提供快速插入和删除操作。用法示例List<String> list = new ArrayList<>(); list.add("Java"); list.add("Kotlin"); System.out.println(list); // 输出: [Java, Kotlin]SetSet是一个不允许重复元素的集合。常用实现HashSet:基于HashMap,无序且高效。LinkedHashSet:类似于HashSet,但维护元素插入顺序。TreeSet:基于NavigableMap,可以排序。用法示例Set<String> set = new HashSet<>(); set.add("Apple"); set.add("Banana"); System.out.println(set); // 输出: [Apple, Banana]MapMap存储键值对,映射唯一键到值。常用实现HashMap:基于散列,无序且允许空键和值。LinkedHashMap:类似于HashMap,但维护插入顺序。TreeMap:基于红黑树,可以排序。用法示例Map<String, Integer> map = new HashMap<>(); map.put("One", 1); map.put("Two", 2); System.out.println(map); // 输出: {One=1, Two=2}集合的批量添加Java提供了多种方式来批量添加元素到集合。使用Collections.addAll()List<String> list = new ArrayList<>(); Collections.addAll(list, "Element1", "Element2");使用构造函数批量添加String[] array = {"Element1", "Element2"}; List<String> listFromArray = new ArrayList<>(Arrays.asList(array));使用Arrays.asList()List<String> listFromList = new ArrayList<>(Arrays.asList("Element1", "Element2"));示例代码import java.util.*; public class CollectionDemo { public static void main(String[] args) { // List 示例 List<String> stringList = new ArrayList<>(); stringList.add("Item1"); stringList.add("Item2"); System.out.println("List: " + stringList); // Set 示例 Set<Integer> numberSet = new HashSet<>(); numberSet.add(100); numberSet.add(200); System.out.println("Set: " + numberSet); // Map 示例 Map<String, Double> scoreMap = new HashMap<>(); scoreMap.put("Alice", 95.5); scoreMap.put("Bob", 89.0); System.out.println("Map: " + scoreMap); // 批量添加到 List List<String> bulkList = new ArrayList<>(); Collections.addAll(bulkList, "A", "B", "C"); System.out.println("Bulk Added List: " + bulkList); // 使用数组构造 List String[] stringArray = {"X", "Y", "Z"}; List<String> listFromArray = new ArrayList<>(Arrays.asList(stringArray)); System.out.println("List from Array: " + listFromArray); } }
2024年08月09日
20 阅读
0 评论
0 点赞
2024-08-08
Java常用API与类
常用API常用的API,StringBuilder、StringBuffer的API是完全一致的(1)StringBuffer append(xx):拼接,追加(2)StringBuffer insert(int index, xx):在[index]位置插入xx(3)StringBuffer delete(int start, int end):删除[start,end)之间字符StringBuffer deleteCharAt(int index):删除[index]位置字符(4)void setCharAt(int index, xx):替换[index]位置字符(5)StringBuffer reverse():反转(6)void setLength(int newLength) :设置当前字符序列长度为newLength(7)StringBuffer replace(int start, int end, String str):替换[start,end)范围的字符序列为str(8)int indexOf(String str):在当前字符序列中查询str的第一次出现下标 int indexOf(String str, int fromIndex):在当前字符序列[fromIndex,最后]中查询str的第一次出现下标 int lastIndexOf(String str):在当前字符序列中查询str的最后一次出现下标 int lastIndexOf(String str, int fromIndex):在当前字符序列[fromIndex,最后]中查询str的最后一次出现下标(9)String substring(int start):截取当前字符序列[start,最后](10)String substring(int start, int end):截取当前字符序列[start,end)(11)String toString():返回此序列中数据的字符串表示形式 @Test public void test6(){ StringBuilder s = new StringBuilder("helloworld"); s.setLength(30); System.out.println(s); } @Test public void test5(){ StringBuilder s = new StringBuilder("helloworld"); s.setCharAt(2, 'a'); System.out.println(s); } @Test public void test4(){ StringBuilder s = new StringBuilder("helloworld"); s.reverse(); System.out.println(s); } @Test public void test3(){ StringBuilder s = new StringBuilder("helloworld"); s.delete(1, 3); s.deleteCharAt(4); System.out.println(s); } @Test public void test2(){ StringBuilder s = new StringBuilder("helloworld"); s.insert(5, "java"); s.insert(5, "chailinyan"); System.out.println(s); } @Test public void test1(){ StringBuilder s = new StringBuilder(); s.append("hello").append(true).append('a').append(12).append("atguigu"); System.out.println(s); System.out.println(s.length()); }效率测试/* * Runtime:JVM运行时环境 * Runtime是一个单例的实现 */ public class TestTime { public static void main(String[] args) { // testStringBuilder(); testStringBuffer(); // testString(); } public static void testString(){ long start = System.currentTimeMillis(); String s = new String("0"); for(int i=1;i<=10000;i++){ s += i; } long end = System.currentTimeMillis(); System.out.println("String拼接+用时:"+(end-start));//444 } public static void testStringBuilder(){ long start = System.currentTimeMillis(); StringBuilder s = new StringBuilder("0"); for(int i=1;i<=10000;i++){ s.append(i); } long end = System.currentTimeMillis(); System.out.println("StringBuilder拼接+用时:"+(end-start));//4 } public static void testStringBuffer(){ long start = System.currentTimeMillis(); StringBuffer s = new StringBuffer("0"); for(int i=1;i<=10000;i++){ s.append(i); } long end = System.currentTimeMillis(); System.out.println("StringBuffer拼接+用时:"+(end-start));//7 } }和数学相关的类Math类java.lang.Math 类包含用于执行基本数学运算的方法,如初等指数、对数、平方根和三角函数。类似这样的工具类,其所有方法均为静态方法,并且不会创建对象,调用起来非常简单。public static final double PI:返回圆周率double pi = Math.PI;public static double abs(double a) :返回 double 值的绝对值。double d1 = Math.abs(-5); //d1的值为5 double d2 = Math.abs(5); //d2的值为5public static double ceil(double a) :返回大于等于参数的最小的整数。double d1 = Math.ceil(3.3); //d1的值为 4.0 double d2 = Math.ceil(-3.3); //d2的值为 -3.0 double d3 = Math.ceil(5.1); //d3的值为 6.0public static double floor(double a) :返回小于等于参数最大的整数。double d1 = Math.floor(3.3); //d1的值为3.0 double d2 = Math.floor(-3.3); //d2的值为-4.0 double d3 = Math.floor(5.1); //d3的值为 5.0public static long round(double a) :返回最接近参数的 long。(相当于四舍五入方法)long d1 = Math.round(5.5); //d1的值为6.0 long d2 = Math.round(5.4); //d2的值为5.0public static double pow(double a,double b):返回a的b幂次方法public static double sqrt(double a):返回a的平方根public static double random():返回[0,1)的随机值public static double max(double x, double y):返回x,y中的最大值public static double min(double x, double y):返回x,y中的最小值double result = Math.pow(2,31); double sqrt = Math.sqrt(256); double rand = Math.random();java.util.Random用于产生随机数public Random():创建一个新的随机数生成器。此构造方法将随机数生成器的种子设置为某个值,该值与此构造方法的所有其他调用所用的值完全不同。(没有真正的随机数,需要种子产生随机数,同一个种子产生的伪随机数序列相同)public Random(long seed):使用单个 long 种子创建一个新的随机数生成器。该种子是伪随机数生成器的内部状态的初始值,该生成器可通过方法 next(int) 维护。boolean nextBoolean():返回下一个伪随机数,它是取自此随机数生成器序列的均匀分布的 boolean 值。void nextBytes(byte[] bytes):生成随机字节并将其置于用户提供的 byte 数组中。double nextDouble():返回下一个伪随机数,它是取自此随机数生成器序列的、在 0.0 和 1.0 之间均匀分布的 double 值。float nextFloat():返回下一个伪随机数,它是取自此随机数生成器序列的、在 0.0 和 1.0 之间均匀分布的 float 值。int nextInt():返回下一个伪随机数,它是此随机数生成器的序列中均匀分布的 int 值。int nextInt(int n):返回一个伪随机数,它是取自此随机数生成器序列的、在 0(包括)和指定值(不包括)之间均匀分布的 int 值。long nextLong():返回下一个伪随机数,它是取自此随机数生成器序列的均匀分布的 long 值。 @Test public void test03(){ Random r = new Random(); System.out.println("随机整数:" + r.nextInt()); System.out.println("随机小数:" + r.nextDouble()); System.out.println("随机布尔值:" + r.nextBoolean()); }BigInteger不可变的任意精度的整数。BigInteger(String val)BigInteger add(BigInteger val)BigInteger subtract(BigInteger val)BigInteger multiply(BigInteger val)BigInteger divide(BigInteger val)BigInteger remainder(BigInteger val)int intValue():将此 BigInteger 转换为 int。long longValue():将此 BigInteger 转换为 long。float floatValue():将此 BigInteger 转换为 float。.... @Test public void test01(){ // long bigNum = 123456789123456789123456789L; BigInteger b1 = new BigInteger("123456789123456789123456789"); BigInteger b2 = new BigInteger("78923456789123456789123456789"); // System.out.println("和:" + (b1+b2));//错误的,无法直接使用+进行求和 System.out.println("和:" + b1.add(b2)); System.out.println("减:" + b1.subtract(b2)); System.out.println("乘:" + b1.multiply(b2)); System.out.println("除:" + b2.divide(b1)); System.out.println("余:" + b2.remainder(b1)); }BigDecimal不可变的、任意精度的有符号十进制数。BigDecimal(String val)BigDecimal add(BigDecimal val)BigDecimal subtract(BigDecimal val)BigDecimal multiply(BigDecimal val)BigDecimal divide(BigDecimal val)BigDecimal divide(BigDecimal divisor, int roundingMode)BigDecimal divide(BigDecimal divisor, int scale, RoundingMode roundingMode)BigDecimal remainder(BigDecimal val)double doubleValue():将此 BigDecimal 转换为 double。.... @Test public void test02(){ /*double big = 12.123456789123456789123456789; System.out.println("big = " + big);*/ BigDecimal b1 = new BigDecimal("123.45678912345678912345678912345678"); BigDecimal b2 = new BigDecimal("7.8923456789123456789123456789998898888"); // System.out.println("和:" + (b1+b2));//错误的,无法直接使用+进行求和 System.out.println("和:" + b1.add(b2)); System.out.println("减:" + b1.subtract(b2)); System.out.println("乘:" + b1.multiply(b2)); System.out.println("除:" + b1.divide(b2,20,RoundingMode.UP));//divide(BigDecimal divisor, int scale, int roundingMode) System.out.println("除:" + b1.divide(b2,20,RoundingMode.DOWN));//divide(BigDecimal divisor, int scale, int roundingMode) System.out.println("余:" + b1.remainder(b2)); } //保留两位小数的方式: @Test public void test02(){ double f = 111231.5585; BigDecimal bg = new BigDecimal(f); double f1 = bg.setScale(2, BigDecimal.ROUND_HALF_UP).doubleValue(); System.out.println(f1); }日期时间APIjava.util.Datenew Date():当前系统时间long getTime():返回该日期时间对象距离1970-1-1 0.0.0 0毫秒之间的毫秒值new Date(long 毫秒):把该毫秒值换算成日期时间对象 @Test public void test5(){ long time = Long.MAX_VALUE; Date d = new Date(time); System.out.println(d); } @Test public void test4(){ long time = 1559807047979L; Date d = new Date(time); System.out.println(d); } @Test public void test3(){ Date d = new Date(); long time = d.getTime(); System.out.println(time);//1559807047979 } @Test public void test2(){ long time = System.currentTimeMillis(); System.out.println(time);//1559806982971 //当前系统时间距离1970-1-1 0:0:0 0毫秒的时间差,毫秒为单位 } @Test public void test1(){ Date d = new Date(); System.out.println(d);//Sun May 12 08:11:15 CST(China Standard Time ) 2024 }java.text.SimpleDateFormatSimpleDateFormat用于日期时间的格式化。 @Test public void test10() throws ParseException{ String str = "2019年06月06日 16时03分7秒 545毫秒 星期四 +0800"; SimpleDateFormat sf = new SimpleDateFormat("yyyy年MM月dd日 HH时mm分ss秒 SSS毫秒 E Z"); Date d = sf.parse(str); System.out.println(d); } @Test public void test9(){ Date d = new Date(); SimpleDateFormat sf = new SimpleDateFormat("yyyy年MM月dd日 HH时mm分ss秒 SSS毫秒 E Z"); //把Date日期转成字符串,按照指定的格式转 String str = sf.format(d); System.out.println(str); }JDK8后日期类Java1.0中包含了一个Date类,但是它的大多数方法已经在Java 1.1引入Calendar类之后被弃用了。而Calendar并不比Date好多少。它们面临的问题是:可变性:象日期和时间这样的类对象应该是不可变的。偏移性:Date中的年份是从1900开始的,而月份都是从0开始的。格式化:格式化只对Date有用可以说,对日期和时间的操作一直是Java程序员最痛苦的地方之一。第三次引入的API是成功的,并且java 8中引入的java.time API 已经纠正了过去的缺陷,将来很长一段时间内它都会为我们服务。新的 java.time 中包含了所有关于时钟(Clock),本地日期(LocalDate)、本地时间(LocalTime)、本地日期时间(LocalDateTime)、时区(ZonedDateTime)和持续时间(Duration)的类。1、LocalDate、LocalTime、LocalDateTime本地日期时间类方法描述now() / now(ZoneId zone)静态方法,根据当前时间创建对象/指定时区的对象of()静态方法,根据指定日期/时间创建对象getDayOfMonth()/getDayOfYear()获得月份天数(1-31) /获得年份天数(1-366)getDayOfWeek()获得星期几(返回一个 DayOfWeek 枚举值)getMonth()获得月份, 返回一个 Month 枚举值getMonthValue() / getYear()获得月份(1-12) /获得年份getHours()/getMinute()/getSecond()获得当前对象对应的小时、分钟、秒withDayOfMonth()/withDayOfYear()/withMonth()/withYear()将月份天数、年份天数、月份、年份修改为指定的值并返回新的对象with(TemporalAdjuster t)将当前日期时间设置为校对器指定的日期时间plusDays(), plusWeeks(), plusMonths(), plusYears(),plusHours()向当前对象添加几天、几周、几个月、几年、几小时minusMonths() / minusWeeks()/minusDays()/minusYears()/minusHours()从当前对象减去几月、几周、几天、几年、几小时plus(TemporalAmount t)/minus(TemporalAmount t)添加或减少一个 Duration 或 PeriodisBefore()/isAfter()比较两个 LocalDateisLeapYear()判断是否是闰年(在LocalDate类中声明)format(DateTimeFormatter t)格式化本地日期、时间,返回一个字符串parse(Charsequence text)将指定格式的字符串解析为日期、时间 @Test public void test7(){ LocalDate now = LocalDate.now(); LocalDate before = now.minusDays(100); System.out.println(before);//2019-02-26 } @Test public void test06(){ LocalDate lai = LocalDate.of(2019, 5, 13); LocalDate go = lai.plusDays(160); System.out.println(go);//2019-10-20 } @Test public void test05(){ LocalDate lai = LocalDate.of(2019, 5, 13); System.out.println(lai.getDayOfYear()); } @Test public void test04(){ LocalDate lai = LocalDate.of(2019, 5, 13); System.out.println(lai); } @Test public void test03(){ LocalDateTime now = LocalDateTime.now(); System.out.println(now); } @Test public void test02(){ LocalTime now = LocalTime.now(); System.out.println(now); } @Test public void test01(){ LocalDate now = LocalDate.now(); System.out.println(now); }2、瞬时:Instant、ZondId和ZonedDateTimeimport java.time.ZoneId; import java.time.ZonedDateTime; import java.util.Set; public class TestZone { @Test public void test01() { //需要知道一些时区的id //Set<String>是一个集合,容器 Set<String> availableZoneIds = ZoneId.getAvailableZoneIds(); //快捷模板 for (String availableZoneId : availableZoneIds) { System.out.println(availableZoneId); } } @Test public void test02(){ ZonedDateTime t1 = ZonedDateTime.now(); System.out.println(t1); ZonedDateTime t2 = ZonedDateTime.now(ZoneId.of("America/New_York")); System.out.println(t2); } @Test public void test03(){ /* Instant代表时间线上的一个瞬时点,是自1970年1月1日0时0分0秒(UTC)以来的秒数(精确到纳秒)。 它主要用于需要高精度时间戳的场景,如记录事件发生的时间点。 */ Instant t = Instant.now(); System.out.println(t); } 3、持续日期/时间:Period和DurationPeriod:用于计算两个“日期”间隔public static void main(String[] args) { LocalDate t1 = LocalDate.now(); LocalDate t2 = LocalDate.of(2018, 12, 31); Period between = Period.between(t1, t2); System.out.println(between); System.out.println("相差的年数:"+between.getYears());//1年 System.out.println("相差的月数:"+between.getMonths());//又7个月 System.out.println("相差的天数:"+between.getDays());//零25天 System.out.println("相差的总数:"+between.toTotalMonths());//总共19个月 }Duration:用于计算两个“时间”间隔 public static void main(String[] args) { LocalDateTime t1 = LocalDateTime.now(); LocalDateTime t2 = LocalDateTime.of(2017, 8, 29, 0, 0, 0, 0); Duration between = Duration.between(t1, t2); System.out.println(between); System.out.println("相差的总天数:"+between.toDays()); System.out.println("相差的总小时数:"+between.toHours()); System.out.println("相差的总分钟数:"+between.toMinutes()); System.out.println("相差的总秒数:"+between.getSeconds()); System.out.println("相差的总毫秒数:"+between.toMillis()); System.out.println("相差的总纳秒数:"+between.toNanos()); System.out.println("不够一秒的纳秒数:"+between.getNano()); }4、DateTimeFormatter:日期时间格式化该类提供了三种格式化方法:预定义的标准格式。如:DateTimeFormatter.ISO_DATE_TIME; ISO_DATE本地化相关的格式。如:DateTimeFormatter.ofLocalizedDate(FormatStyle.MEDIUM)自定义的格式。如:DateTimeFormatter.ofPattern(“yyyy-MM-dd hh:mm:ss”) @Test public void test(){ LocalDateTime now = LocalDateTime.now(); //预定义的标准格式 DateTimeFormatter df = DateTimeFormatter.ISO_DATE_TIME;//2019-06-06T16:38:23.756 //格式化操作 String str = df.format(now); System.out.println(str); } @Test public void test1(){ LocalDateTime now = LocalDateTime.now(); //本地化相关的格式 // DateTimeFormatter df = DateTimeFormatter.ofLocalizedDateTime(FormatStyle.LONG);//2019年6月6日 下午04时40分03秒 DateTimeFormatter df = DateTimeFormatter.ofLocalizedDateTime(FormatStyle.SHORT);//19-6-6 下午4:40 //格式化操作 String str = df.format(now); System.out.println(str); } @Test public void test2(){ LocalDateTime now = LocalDateTime.now(); //自定义的格式 DateTimeFormatter df = DateTimeFormatter.ofPattern("yyyy年MM月dd日 HH时mm分ss秒 SSS毫秒 E 是这一年的D天"); //格式化操作 String str = df.format(now); System.out.println(str); } //把字符串解析为日期对象 public void test3(){ //自定义的格式 DateTimeFormatter pattern = DateTimeFormatter.ofPattern("yyyy.MM.dd"); //解析操作 LocalDate parse = LocalDate.parse("2020.12.12", pattern); System.out.println(parse); }系统相关类java.lang.System 类System 类提供了一系列与系统操作相关的方法。方法列表static long currentTimeMillis(): 返回当前系统时间距离 1970-1-1 0:0:0 的毫秒值。static void exit(int status): 退出当前系统。static void gc(): 运行垃圾回收器。static String getProperty(String key): 获取某个系统属性。static native void arraycopy(Object src, int srcPos, Object dest, int destPos, int length): 用于数组之间的复制。示例代码public class SystemTest { @Test public void test04(){ System.out.println("1111"); // 终止 JVM System.exit(0); // 下面的代码不会被执行,因为 JVM 已经终止 System.out.println("3333"); } @Test public void test03(){ // 获取系统属性 Properties properties = System.getProperties(); String javaVersion = properties.getProperty("java.version"); System.out.println("Java Version: " + javaVersion); } @Test public void test02(){ // 获取当前时间毫秒数 long timeMillis = System.currentTimeMillis(); System.out.println("Current Time in Millis: " + timeMillis); } @Test public void test01() { int[] arr = {66, 77, 99, 33}; int[] newArr = new int[arr.length + (arr.length >> 1)]; System.arraycopy(arr, 0, newArr, 1, arr.length); System.out.println("Array after copy: " + Arrays.toString(newArr)); } }java.lang.Runtime 类Runtime 类使应用程序能够与其运行的环境相连接。方法列表public static Runtime getRuntime(): 返回与当前 Java 应用程序相关的运行时对象。public long totalMemory(): 返回 Java 虚拟机中的内存总量。public long freeMemory(): 返回 Java 虚拟机中的空闲内存量。public long maxMemory(): 返回 Java 虚拟机试图使用的最大内存量。数组工具类java.util.Arrays 类Arrays 类提供了一系列静态方法来操作数组。方法列表static int binarySearch(int[] a, int key): 在有序数组中查找元素。static int[] copyOf(int[] original, int newLength): 复制数组,改变长度。static int[] copyOfRange(int[] original, int from, int to): 复制数组的一部分。static boolean equals(int[] a, int[] a2): 比较两个数组是否相等。static void fill(int[] a, int val): 使用指定值填充整个数组。static void fill(int[] a, int fromIndex, int toIndex, int val): 使用指定值填充数组的一部分。static void sort(int[] a): 对数组进行排序。static void sort(int[] a, int fromIndex, int toIndex): 对数组的一部分进行排序。static String toString(int[] a): 将数组转换为字符串。示例代码import java.util.Arrays; import java.util.Random; public class Test { public static void main(String[] args) { int[] arr = new int[5]; System.out.println("Initial array: " + Arrays.toString(arr)); Arrays.fill(arr, 3); System.out.println("Array after fill: " + Arrays.toString(arr)); Random rand = new Random(); for (int i = 0; i < arr.length; i++) { arr[i] = rand.nextInt(100); } System.out.println("Array after random fill: " + Arrays.toString(arr)); int[] arr2 = Arrays.copyOf(arr, 10); System.out.println("Copied array: " + Arrays.toString(arr2)); System.out.println("Are arrays equal? " + Arrays.equals(arr, arr2)); Arrays.sort(arr); System.out.println("Sorted array: " + Arrays.toString(arr)); } }请注意,示例代码中的 System.exit(0) 调用会导致程序终止,因此在其后的代码将不会被执行。如果您希望测试代码能够继续执行,不应在测试方法中使用 System.exit(0)。
2024年08月08日
20 阅读
0 评论
0 点赞
2024-08-08
异常类Throwable
Throwable结构Error:严重错误Error,无法人为处理的错误。例如: StackOverflowError、OutOfMemoryError。Exception:表示异常,其它因编程错误或偶然的外在因素导致的一般性问题。例如:空指针访问、试图读取不存在的文件、网络连接中断、数组角标越界Throwable中的常用方法:public void printStackTrace():打印异常的详细信息。包含了异常的类型,异常的原因,还包括异常出现的位置,在开发和调试阶段,都得使用printStackTrace。public String getMessage():获取发生异常的原因。提示给用户的时候,就提示错误原因。出现异常,不要紧张,把异常的简单类名,拷贝到API中去查。异常分类由于Error情况发生是我们无法处理的,一般因为是内存不足或程序存在严重逻辑问题,只能通过扩大内存或重新修改代码解决。平常所遇到的大多数是Exception类型异常,通常分两大类:非受查异常 运行期异常(unchecked Exception):这类异常的发生多数是因为程序员编写的代码逻辑不够严谨造成的(如数组脚标越界异常),可以选择进行处理或不处理,最好是通过修正、优化代码避免异常的发生(或者使用异常处理简化复杂的逻辑判断代码)。 受查异常 编译期异常(checked Exception):这类异常一般由程序之外的因素引起的(如程序读取的文件不存在、网络中断),而不是程序员写的代码逻辑有问题,所以程序员容易忽略对这类异常的处理,而恰恰这类异常又很常发生,所以Java要求针对这类可能发生的异常必须进行处理,否则编译无法通过。(只有java语言有需强制处理的异常)常见的错误和异常演示示例VirtualMachineError最常见的就是:StackOverflowError:虚拟机栈内存不足,无法分配栈帧所需空间。OutOfMemoryError:没有足够的内存空间可以分配。 @Test public void test01(){ //StackOverflowError digui(); } public void digui(){ digui(); } @Test public void test02(){ //OutOfMemoryError //方式一: int[] arr = new int[Integer.MAX_VALUE]; } @Test public void test03(){ //OutOfMemoryError //方式二: StringBuilder s = new StringBuilder(); while(true){ s.append("atguigu"); } }运行时异常 @Test public void test01(){ //NullPointerException int[] arr=null; System.out.println(arr.length); } @Test public void test02(){ //ClassCastException Person p = new Man(); Woman w = (Woman) p; } @Test public void test03(){ //ArrayIndexOutOfBoundsException int[] arr = new int[5]; for (int i = 1; i <= 5; i++) { System.out.println(arr[i]); } } @Test public void test04(){ //InputMismatchException Scanner input = new Scanner(System.in); System.out.print("请输入一个整数:"); int num = input.nextInt(); } @Test public void test05(){ int a = 1; int b = 0; //ArithmeticException System.out.println(a/b); }编译时异常 @Test public void test06() throws InterruptedException{ Thread.sleep(1000);//休眠1秒 } @Test public void test07() throws FileNotFoundException{ FileInputStream fis = new FileInputStream("Java学习秘籍.txt"); } @Test public void test08() throws SQLException{ Connection conn = DriverManager.getConnection("...."); }9.4 异常的生成与抛出机制throw执行过程中如出现异常,会生成一个异常类对象,然后该异常对象会被提交给Java运行时系统,这个过程称为抛出(throw)异常。异常对象的生成与抛出有两种方式:由虚拟机自动生成:程序运行过程中,虚拟机检测到程序发生了问题,就会在后台自动创建一个对应异常类的实例对象并自动抛出。我们通过示例分析下一次产生的过程:运行以下程序会产生一个数组索引越界异常ArrayIndexOfBoundsException。// 工具类 public class ArrayTools { // 对给定的数组通过给定的角标获取元素。 public static int getElement(int[] arr, int index) { int element = arr[index]; return element; } }// 测试类 public class ExceptionDemo { public static void main(String[] args) { int[] arr = { 34, 5, 67 }; intnum = ArrayTools.getElement(arr, 4) System.out.println("num=" + num); System.out.println("over"); } }由此看出,异常对象被JVM创建后,在产生异常的方法中会自动抛出,抛给方法的调用者,抛给main方法,最后抛给虚拟机,虚拟机打印异常信息后终止程序。由开发人员手动创建:Exception exception = new ClassCastException();——创建好的异常对象不抛出对程序没有任何影响,和创建一个普通对象一样,手动创建的异常对象需要手动抛出,才会对程序产生影响。在Java中,使用关throw关键字手动抛出一个异常对象,throw用在方法内,将这个异常对象传递到方法调用者处,同时结束当前方法的执行。使用格式:throw new 异常类名(参数);例如:throw new NullPointerException("要访问的arr数组不存在"); throw new ArrayIndexOutOfBoundsException("该索引在数组中不存在,已超出范围");throw的使用示例:public class ThrowDemo { public static void main(String[] args) { //创建一个数组 int[] arr = {2,4,52,2}; //根据索引找对应的元素 int index = 4; int element = getElement(arr, index); System.out.println(element); System.out.println("over"); } /* * 根据 索引找到数组中对应的元素 */ public static int getElement(int[] arr,int index){ if(arr == null){ /* 判断条件如果满足,当执行完throw抛出异常对象后,方法已经无法继续运算。 这时就会结束当前方法的执行,并将异常告知给调用者。这时就需要通过异常来解决。 */ throw new NullPointerException("要访问的arr数组不存在"); } //判断 索引是否越界 if(index<0 || index>arr.length-1){ /* 判断条件如果满足,当执行完throw抛出异常对象后,方法已经无法继续运算。 这时就会结束当前方法的执行,并将异常告知给调用者。这时就需要通过异常来解决。 */ throw new ArrayIndexOutOfBoundsException("哥们,角标越界了~~~"); } int element = arr[index]; return element; } }注意:如果产生了问题,我们就会throw将问题描述类即异常进行抛出,也就是将问题返回给该方法的调用者。那么对于调用者来说,该怎么处理呢?一种是进行捕获处理,另一种就是继续将问题声明出去,使用throws声明处理。异常的处理机制如果一个方法内抛出异常,该异常对象会被抛给调用者方法中处理。如果异常没有在调用者方法中处理,它继续被抛给这个调用方法的上层方法。这个过程将一直继续下去,直到异常被处理。这一过程称为捕获(catch)异常。捕获异常try…catch捕获异常:Java中对异常有针对性的语句进行捕获,可以对出现的异常进行指定方式的处理。捕获异常语法如下:try{ 编写可能会出现异常的代码 }catch(异常类型1 e){ 处理异常的代码 //记录日志/打印异常信息/继续抛出异常 }catch(异常类型2 e){ 处理异常的代码 //记录日志/打印异常信息/继续抛出异常 } ....try:捕获异常的第一步是用try{…}语句块选定捕获异常的范围,将可能出现异常的代码放在try语句块中。建议:此范围尽量小。catch:用来进行某种异常的捕获,实现对捕获到的异常进行处理。每个try语句块可以伴随一个或多个catch语句,用于处理可能产生的不同类型的异常。可以有多个catch块,按顺序匹配。如果多个异常类型有包含关系,那么小上大下演示示例:public class TestException { public static void main(String[] args) { try { readFile("不敲代码学会Java秘籍.txt"); } catch (FileNotFoundException e) { // e.printStackTrace(); // System.out.println("好好敲代码,不要老是想获得什么秘籍"); System.out.println(e.getMessage()); } catch (IllegalAccessException e) { e.printStackTrace(); } System.out.println("继续学习吧..."); } // 如果定义功能时有问题发生需要报告给调用者。可以通过在方法上使用throws关键字进行声明 public static void readFile(String filePath) throws FileNotFoundException, IllegalAccessException{ File file = new File(filePath); if(!file.exists()){ throw new FileNotFoundException(filePath+"文件不存在"); } if(!file.isFile()){ throw new IllegalAccessException(filePath + "不是文件,无法直接读取"); } //... } }获取异常信息:捕获到了异常对象,就可以获取异常对象中封装的异常信息,Throwable类中定义了一些方法用于获取异常对象中的信息:public String getMessage():获取异常的描述信息。public void printStackTrace():打印异常的跟踪栈信息并输出到控制台。这些信息包含了异常的类型,异常信息,还包括异常出现的位置,在开发和调试阶段,建议使用printStackTrace。finally块finally:在finally代码块中存放的代码都是一定会被执行的。由于异常会引发程序跳转,导致后面有些语句执行不到,如果一定要执行这些语句就可以使用finally,finally常用于释放系统资源。比如:当我们在try语句块中打开了一些物理资源(磁盘文件/网络连接/数据库连接等),无论异常有没有发生,我们都要在使用完之后关闭已打开的资源,避免系统资源的浪费。finally的语法: try{ }catch(...){ }finally{ 无论try中是否发生异常,也无论catch是否捕获异常,也不管try和catch中是否有return语句,都一定会执行 } 或 try{ }finally{ 无论try中是否发生异常,也不管try中是否有return语句,都一定会执行。 } 注意:finally不能单独使用。当只有在try或者catch中调用退出JVM的相关方法,例如System.exit(0),此时finally才不会执行,否则finally永远会执行。finally代码IO流读取文件示例如下:(暂)import java.io.File; import java.io.FileInputStream; import java.io.FileNotFoundException; import java.io.IOException; public class TestException { public static void main(String[] args) { readFile("不敲代码学会Java秘籍.txt"); System.out.println("继续学习吧..."); } // 如果定义功能时有问题发生需要报告给调用者。可以通过在方法上使用throws关键字进行声明 public static void readFile(String filePath) { File file = new File(filePath); FileInputStream fis = null; try { if(!file.exists()){ throw new FileNotFoundException(filePath+"文件不存在"); } if(!file.isFile()){ throw new IllegalAccessException(filePath + "不是文件,无法直接读取"); } fis = new FileInputStream(file); //... } catch (Exception e) { //抓取到的是编译期异常 抛出去的是运行期 throw new RuntimeException(e); }finally{ System.out.println("无论如何,这里的代码一定会被执行"); try { if(fis!=null){ fis.close(); } } catch (IOException e) { //抓取到的是编译期异常 抛出去的是运行期 throw new RuntimeException(e); } } } }声明异常throwsthrows:用在方法上,表明此方法可能会产生的异常类型。如果在某方法内通过抛出了必须要处理的编译期异常,有两种选择:要么在当前方法进行捕获处理,要么通过throws在当前方法上进行声明,让方法的调用者去处理。声明异常格式:修饰符 返回值类型 方法名(参数) throws 异常类名1,异常类名2…{ } 声明异常的代码演示:import java.io.File; import java.io.FileNotFoundException; public class TestException { public static void main(String[] args) throws FileNotFoundException { readFile("不敲代码学会Java秘籍.txt"); } // 如果定义功能时有问题发生需要报告给调用者。可以通过在方法上使用throws关键字进行声明 public static void readFile(String filePath) throws FileNotFoundException{ File file = new File(filePath); if(!file.exists()){ throw new FileNotFoundException(filePath+"文件不存在"); } } }throws用于进行异常类的声明,若该方法可能有多种异常情况产生,那么在throws后面可以写多个异常类,用逗号隔开。import java.io.File; import java.io.FileNotFoundException; public class TestException { public static void main(String[] args) throws FileNotFoundException,IllegalAccessException { readFile("不敲代码学会Java秘籍.txt"); } // 如果定义功能时有问题发生需要报告给调用者。可以通过在方法上使用throws关键字进行声明 public static void readFile(String filePath) throws FileNotFoundException,IllegalAccessException{ File file = new File(filePath); if(!file.exists()){ throw new FileNotFoundException(filePath+"文件不存在"); } if(!file.isFile()){ throw new IllegalAccessException(filePath + "不是文件,无法直接读取"); } //... } }附录歌名:《死了都要try》死了都要try不抓住异常不痛快BUG多深,只有这样,才不用重来死了都要try不catch够我不痛快程序毁灭throw还在把每天,当作程序来更改一改一天,都累到泪水掉下来不理会,老板是看好或看坏只要有工资,来还贷改,不是需求做的太坏那是客户想要什么,自己都不明白忍受现在,自己一生都还不完的贷很多模块,不能完成,我还得改死了都要try不catch够我不痛快程序毁灭throw还在
2024年08月08日
14 阅读
0 评论
0 点赞
2024-08-07
经典接口
经典接口介绍java.lang.Comparable我们知道基本数据类型的数据(除boolean类型外)需要比较大小的话,之间使用比较运算符即可,但是引用数据类型是不能直接使用比较运算符来比较大小的。那么,如何解决这个问题呢?Java给所有引用数据类型的大小比较,指定了一个标准接口,就是java.lang.Comparable接口:package java.lang; public interface Comparable{ int compareTo(Object obj); }那么我们想要使得我们某个类的对象可以比较大小,怎么做呢?步骤:第一步:哪个类的对象要比较大小,哪个类就实现java.lang.Comparable接口,并重写方法方法体就是你要如何比较当前对象和指定的另一个对象的大小第二步:对象比较大小时,通过对象调用compareTo方法,根据方法的返回值决定谁大谁小。this对象(调用compareTo方法的对象)大于指定对象(传入compareTo()的参数对象)返回正整数this对象(调用compareTo方法的对象)小于指定对象(传入compareTo()的参数对象)返回负整数this对象(调用compareTo方法的对象)等于指定对象(传入compareTo()的参数对象)返回零代码示例:package com.atguigu.api; public class Student implements Comparable { private int id; private String name; private int score; private int age; public Student(int id, String name, int score, int age) { this.id = id; this.name = name; this.score = score; this.age = age; } 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; } public int getScore() { return score; } public void setScore(int score) { this.score = score; } public int getAge() { return age; } public void setAge(int age) { this.age = age; } @Override public String toString() { return "Student{" + "id=" + id + ", name='" + name + '\'' + ", score=" + score + ", age=" + age + '}'; } @Override public int compareTo(Object o) { //这些需要强制,将o对象向下转型为Student类型的变量,才能调用Student类中的属性 //默认按照学号比较大小 Student stu = (Student) o; return this.id - stu.id; } }测试类package com.atguigu.api; public class TestStudent { public static void main(String[] args) { Student[] arr = new Student[5]; arr[0] = new Student(3,"张三",90,23); arr[1] = new Student(1,"熊大",30,22); arr[2] = new Student(5,"王五",75,25); arr[3] = new Student(4,"李四",85,24); arr[4] = new Student(2,"熊二",85,18); //单独比较两个对象 System.out.println(arr[0].compareTo(arr[1])); System.out.println(arr[1].compareTo(arr[2])); System.out.println(arr[2].compareTo(arr[2])); System.out.println("所有学生:"); for (int i = 0; i < arr.length; i++) { System.out.println(arr[i]); } System.out.println("按照学号排序:"); for (int i = 1; i < arr.length; i++) { for (int j = 0; j < arr.length-i; j++) { if(arr[j].compareTo(arr[j+1])>0){ Student temp = arr[j]; arr[j] = arr[j+1]; arr[j+1] = temp; } } } for (int i = 0; i < arr.length; i++) { System.out.println(arr[i]); } } } java.util.Comparator思考:(1)如果一个类,没有实现Comparable接口,而这个类你又不方便修改(例如:一些第三方的类,你只有.class文件,没有源文件),那么这样类的对象也要比较大小怎么办?(2)如果一个类,实现了Comparable接口,也指定了两个对象的比较大小的规则,但是此时此刻我不想按照它预定义的方法比较大小,但是我又不能随意修改,因为会影响其他地方的使用,怎么办?JDK在设计类库之初,也考虑到这种情况了,所以又增加了一个java.util.Comparator接口。package java.util; public interface Comparator{ int compare(Object o1,Object o2); }那么我们想要比较某个类的两个对象的大小,怎么做呢?步骤:第一步:编写一个类,我们称之为比较器类型,实现java.util.Comparator接口,并重写方法方法体就是你要如何指定的两个对象的大小第二步:比较大小时,通过比较器类型的对象调用compare()方法,将要比较大小的两个对象作为compare方法的实参传入,根据方法的返回值决定谁大谁小。o1对象大于o2返回正整数o1对象小于o2返回负整数o1对象等于o2返回零代码示例:定义定制比较器类package com.atguigu.api; import java.util.Comparator; public class StudentScoreComparator implements Comparator { @Override public int compare(Object o1, Object o2) { Student s1 = (Student) o1; Student s2 = (Student) o2; int result = s1.getScore() - s2.getScore(); return result != 0 ? result : s1.getId() - s2.getId(); } }代码示例:测试类package com.atguigu.api; public class TestStudent { public static void main(String[] args) { Student[] arr = new Student[5]; arr[0] = new Student(3,"张三",90,23); arr[1] = new Student(1,"熊大",30,22); arr[2] = new Student(5,"王五",75,25); arr[3] = new Student(4,"李四",85,24); arr[4] = new Student(2,"熊二",85,18); //单独比较两个对象 System.out.println(arr[0].compareTo(arr[1])); System.out.println(arr[1].compareTo(arr[2])); System.out.println(arr[2].compareTo(arr[2])); System.out.println("所有学生:"); for (int i = 0; i < arr.length; i++) { System.out.println(arr[i]); } System.out.println("按照学号排序:"); for (int i = 1; i < arr.length; i++) { for (int j = 0; j < arr.length-i; j++) { if(arr[j].compareTo(arr[j+1])>0){ Student temp = arr[j]; arr[j] = arr[j+1]; arr[j+1] = temp; } } } for (int i = 0; i < arr.length; i++) { System.out.println(arr[i]); } System.out.println("按照成绩排序"); StudentScoreComparator sc = new StudentScoreComparator(); for (int i = 1; i < arr.length; i++) { for (int j = 0; j < arr.length-i; j++) { if(sc.compare(arr[j],arr[j+1])>0){ Student temp = arr[j]; arr[j] = arr[j+1]; arr[j+1] = temp; } } } for (int i = 0; i < arr.length; i++) { System.out.println(arr[i]); } } }Cloneable接口基础详解一、引言Cloneable接口是Java开发中常用的一个接口, 它的作用是使一个类的实例能够将自身拷贝到另一个新的实例中,注意,这里所说的“拷贝”拷的是对象实例,而不是类的定义,进一步说,拷贝的是一个类的实例中各字段的值。在开发过程中,拷贝实例是常见的一种操作,如果一个类中的字段较多,而我们又采用在客户端中逐字段复制的方法进行拷贝操作的话,将不可避免的造成客户端代码繁杂冗长,而且也无法对类中的私有成员进行复制,而如果让需要具备拷贝功能的类实现Cloneable接口,并重写clone()方法,就可以通过调用clone()方法的方式简洁地实现实例拷贝功能。二、源码分析打开JDK源码找到Coloneable接口,发现Cloneable接口竟然没有定义任何的接口方法,这是为什么呢?/* JDK1.8的Cloneable接口源代码 */ public interface Cloneable { } 1.2.3.4.5.Cloneable接口之所以没有定义任何的接口的原因其实很简单,那就是在Java中,Object类已经将clone()方法定义为所有类都应该具有的基本功能,只是将该方法声明为了protected类型。该方法定义了逐字段拷贝实例的操作。/* Object类中clone()是一个native本地方法,因此没有实现体,而且在拷贝字段时,除了Object类的字段外,其子类的新字段也将被拷贝到新的实例中 */ protected native Object clone() throws CloneNotSupportedException; 1.2.3.4.既然Object类中既然已经有了一个定义实例拷贝操作的方法,那为什么还是需要让想具备实力拷贝功能的类实现Cloneable接口呢?其实,Cloneable接口在这里起到了一种标识的作用,表明实现它的类具备了实例拷贝功能,在Cloneable接口的官方javadoc文档中有这样一段话: "Invoking Object's clone method on an instance that does not implement the Cloneable interface results in the exception CloneNotSupportedException being thrown. JDK1.8" "在不实现可克隆接口的实例上调用对象的克隆方法会导致引发异常CloneNotSupportedException。JDK1.8 " 1.2.3.也就是说,要想使一个类具备拷贝实例的功能,那么除了要重写Object类的clone()方法外,还必须要实现Cloneable接口。下面的代码即可以使一个CloneableTest具备了浅拷贝实例的功能。class CloneableTest implements Cloneable { @Override public Object clone() throws CloneNotSupportedException { return super.clone(); } } 1.2.3.4.5.6.7.8.三、浅拷贝与深拷贝浅拷贝前面提到了浅拷贝与深拷贝的概念,这是任何一种面向对象的编程语言中都必须要讨论的内容。在java中,对象创建后需要有一个引用变量来指向该对象实际的地址空间,也就是说引用变量与对象实体是两个不同的数据体。在Object类的clone()方法中,对对象字段进行复制时,如果字段是基本数据类型(如int、double等),则会复制字段的值到一个新的变量中,而字段是引用类型,则仅会将引用值复制给新对象中的相应字段中,也就是说,两个字段指向了同一个对象实例。案例分析:一、先定义一个用户自定义的类Dog,该类中有一个字符串类型的成员变量,该类将用于验证浅拷贝的性质。public class Dog { public String name = null; public Dog(String name) { this.name = name; } } 1.2.3.4.5.6.7.8.二、定义一个具有拷贝实例功能的CloneablePerson类实现Cloneable接口并重写Clone()方法,并在重写方法中调用Object类的本地clone()方法。public class CloneablePerson implements Cloneable { public String name = null;// 引用数据类型 public Dog dog = null;// 引用数据类型 public int age = 0;// 基本数据类型 public CloneablePerson(String name, Dog dog, int age) { this.name = name; this.dog = dog; this.age = age; } public void show() { System.out.println("name=" + name); System.out.println("dog=" + dog.name); System.out.println("age=" + age); } /* 重写clone()方法并调用父类Object的本地clone()方法,实现浅拷贝功能 */ @Override protected Object clone() throws CloneNotSupportedException { return super.clone(); } } 1.2.3.4.5.6.7.8.9.10.11.12.13.14.15.16.17.18.19.20.21.22.23.24.25.26.27.三、在Test类中测试代码:1.创建一个CloneableClass类对象作为初始实例,该实例的各字段值如构造函数所示。2.在调用初始实例的clone()方法创建一个拷贝实例,显示初始实例与拷贝实例各字段的值并判断他们是不是指向了同一个对象实例。public class Test { public static void main(String[] args) throws CloneNotSupportedException { Dog black = new Dog("小黑"); // src 源对象 CloneablePerson src = new CloneablePerson("jh", black, 21); // dest 拷贝目标对象 CloneablePerson dest = null; Object o = src.clone(); if (o instanceof CloneablePerson) { dest = (CloneablePerson) o; } System.out.println("==================浅拷贝测试=================="); System.out.println("dest == src? " + (dest == src)); System.out.println("------------------------------------------"); System.out.println("src的所有数据:"); src.show(); System.out.println("------------------------------------------"); System.out.println("dest的所有数据:"); dest.show(); System.out.println("------------------------------------------"); System.out.println("src.name == dest.name? " + (src.name == dest.name)); System.out.println("src.dog == dest.dog? " + (src.dog == dest.dog)); System.out.println("src.age == dest.age? " + (src.age == dest.age)); } } 1.2.3.4.5.6.7.8.9.10.11.12.13.14.15.16.17.18.19.20.21.22.23.24.25.26.27.28.29.30.31.32.33.执行结果:==================浅拷贝测试================== dest == src? false ------------------------------------------ src的所有数据: name=jh dog=小黑 age=21 ------------------------------------------ dest的所有数据: name=jh dog=小黑 age=21 ------------------------------------------ src.name == dest.name? true src.dog == dest.dog? true src.age == dest.age? true Process finished with exit code 0 1.2.3.4.5.6.7.8.9.10.11.12.13.14.15.16.17.18.从执行结果来看 dest == src? false * 说明dest和src是两个不同的对象实例(对于引用类型的比变量,“==”判断的是对象地址是否相同,也就是是不是指向了同一个对象),但是src.xxx== dest.xxx? true* 说明他们字段的值却是相同的,也证明了本地Object本地clone()对实例引用型字段进行的是浅拷贝。那么浅拷贝会造成什么样的后果呢?由于浅拷贝仅将字段的引用值复制给了新的字段,但是却并没有创建新的相应对象,也就是说dest和src中的两个字段都指向了同一个对象实例。这样,我们对dest中各字段所指向对象的属性进行了修改,src中的成员对象也会随之改变测试代码和运行结果如下所示。public class Test { public static void main(String[] args) throws CloneNotSupportedException { Dog black = new Dog("小黑"); CloneablePerson src = new CloneablePerson("jh", black, 21); CloneablePerson dest = null; Object o = src.clone(); if (o instanceof CloneablePerson) { dest = (CloneablePerson) o; } // 修改dest中各字段指向对象的属性 dest.name = "update"; dest.dog.name= "小白"; dest.age = 18; System.out.println("==================浅拷贝=================="); System.out.println("dest == src? " + (dest == src)); System.out.println("------------------------------------------"); System.out.println("src的所有数据:"); src.show(); System.out.println("------------------------------------------"); System.out.println("dest的所有数据:"); dest.show(); System.out.println("------------------------------------------"); System.out.println("src.name == dest.name? " + (src.name == dest.name)); System.out.println("src.dog == dest.dog? " + (src.dog == dest.dog)); System.out.println("src.age == dest.age? " + (src.age == dest.age)); } } 1.2.3.4.5.6.7.8.9.10.11.12.13.14.15.16.17.18.19.20.21.22.23.24.25.26.27.28.29.30.31.32.33.34.35.36.37.38.运行结果:==================浅拷贝================== dest == src? false ------------------------------------------ src的所有数据: name=jh dog=小白 age=21 ------------------------------------------ dest的所有数据: name=update dog=小白 age=18 ------------------------------------------ src.name == dest.name? false src.dog == dest.dog? true src.age == dest.age? false 1.2.3.4.5.6.7.8.9.10.11.12.13.14.15.16.从运行结果 src.dog == dest.dog? true 可以看出,由于CloneablePerson的dog字段为引用型变量,所以由src浅拷贝出来的dest实例中的dog字段与src中的dog字段实际上指向的是同一个对象,因此dest对成员对象的修改也就导致了src 中相应成员对象的随之改变。深拷贝所谓深拷贝就是对于引用型变量,深拷贝会开辟一块新的内存空间,将被复制引用所指向的对象实例的各个属性复制到新的内存空间中,然后将新的引用指向块内存。( 个人狭隘的理解就是 深拷贝把 对象以及对象属性,普通属性都拷贝一次,放入到新的内存空间。)要想实现深拷贝的功能,在重写clone()方法的时候,就不能再简单地调用Object的本地clone()方法,而是要对上文中的CloneablePerson做如下修改:public class CloneablePerson implements Cloneable { public String name = null; public Dog dog = null; public int age = 0; public CloneablePerson(String name, Dog dog, int age) { this.name = name; this.dog = dog; this.age = age; } public void show() { System.out.println("name=" + name); System.out.println("dog=" + dog.name); System.out.println("age=" + age); } /* 浅拷贝 @Override protected Object clone() throws CloneNotSupportedException { return super.clone(); }*/ // 深拷贝 @Override public Object clone() throws CloneNotSupportedException { String name = this.name; Dog dog = new Dog(this.dog.name); int age = this.age; CloneablePerson dest = new CloneablePerson(name,dog,age); return dest; } } 1.2.3.4.5.6.7.8.9.10.11.12.13.14.15.16.17.18.19.20.21.22.23.24.25.26.27.28.29.30.31.32.33.34.35.按着浅拷贝的测试思路再走一遍:public class Test { public static void main(String[] args) throws CloneNotSupportedException { Dog black = new Dog("小黑"); CloneablePerson src = new CloneablePerson("jh", black, 21); CloneablePerson dest = null; Object o = src.clone(); if (o instanceof CloneablePerson) { dest = (CloneablePerson) o; } // 修改dest中各字段指向对象的属性 dest.name = "update"; dest.dog.name= "小白"; dest.age = 18; System.out.println("==================深拷贝=================="); System.out.println("dest == src? " + (dest == src)); System.out.println("------------------------------------------"); System.out.println("src的所有数据:"); src.show(); System.out.println("------------------------------------------"); System.out.println("dest的所有数据:"); dest.show(); System.out.println("------------------------------------------"); System.out.println("src.name == dest.name? " + (src.name == dest.name)); System.out.println("src.dog == dest.dog? " + (src.dog == dest.dog)); System.out.println("src.age == dest.age? " + (src.age == dest.age)); } } 1.2.3.4.5.6.7.8.9.10.11.12.13.14.15.16.17.18.19.20.21.22.23.24.25.26.27.28.29.30.31.32.33.34.35.36.37.38.运行结果如下:==================深拷贝================== dest == src? false ------------------------------------------ src的所有数据: name=jh dog=小黑 age=21 ------------------------------------------ dest的所有数据: name=update dog=小白 age=18 ------------------------------------------ src.name == dest.name? false src.dog == dest.dog? false src.age == dest.age? false 1.2.3.4.5.6.7.8.9.10.11.12.13.14.15.16.根据以上结果 src.xxx == dest.xxx? false 可以看出 src和dest 所有相同字段都指向了不同的实例,无论怎么修改 dest对象都不会影响src对象,这就是深拷贝。
2024年08月07日
9 阅读
0 评论
0 点赞
2024-08-06
数组操作
数组多个相同类型数据按一定顺序排列的集合,并使用一个名字命名,并通过编号的方式对这些数据进行统一管理。存储相同类型数据的有序集合.数组中的概念数组名下标(或索引)元素数组的长度数组的特点:数组本身是引用数据类型,而数组中的元素可以是任何数据类型。创建数组对象会在内存中开辟一整块连续的空间。占据的空间的大小,取决于数组长度和其中元素的类型。数组中的元素在内存中是依次紧密排列的,有序的。数组,一旦初始化完成,其长度就是确定的。数组的长度一旦确定,就不能修改。我们可以直接通过下标(或索引)的方式调用指定位置的元素,速度很快。数组名中引用的是这块连续空间的首地址。数组的分类1、按照元素类型分:基本数据类型元素的数组:每个元素位置存储基本数据类型的值引用数据类型元素的数组:每个元素位置存储对象(本质是存储对象的首地址)(在面向对象部分讲解)2、按照维度分:一维数组:存储一组数据二维数组:存储多组数据,相当于二维表,一行代表一组数据,只是这里的二维表每一行长度不要求一样。一维数组的声明格式://推荐 元素的数据类型[] 一维数组的名称; //不推荐 元素的数据类型 一维数组名[];举例:int[] arr; int arr1[]; double[] arr2; String[] arr3; //引用类型变量数组数组的声明,需要明确:(1)数组的维度:在Java中数组的符号是[],[]表示一维,[][]表示二维。(2)数组的元素类型:即创建的数组容器可以存储什么数据类型的数据。元素的类型可以是任意的Java的数据类型。例如:int、String、Student等。(3)数组名:就是代表某个数组的标识符,数组名其实也是变量名,按照变量的命名规范来命名。数组名是个引用数据类型的变量,因为它代表一组数据。举例:public class ArrayTest1 { public static void main(String[] args) { //比如,要存储一个小组的成绩 int[] scores; int grades[]; // System.out.println(scores);//未初始化不能使用 //比如,要存储一组字母 char[] letters; //比如,要存储一组姓名 String[] names; //比如,要存储一组价格 double[] prices; } }注意:Java语言中声明数组时不能指定其长度(数组中元素的个数)。 例如: int a[5]; //非法维数组的初始化静态初始化如果数组变量的初始化和数组元素的赋值操作同时进行,那就称为静态初始化。静态初始化,本质是用静态数据(编译时已知)为数组初始化。此时数组的长度由静态数据的个数决定。一维数组声明和静态初始化格式1:数据类型[] 数组名 = new 数据类型[]{元素1,元素2,元素3,...}; 或 数据类型[] 数组名; 数组名 = new 数据类型[]{元素1,元素2,元素3,...};new:关键字,创建数组使用的关键字。因为数组本身是引用数据类型,所以要用new创建数组实体。例如,定义存储1,2,3,4,5整数的数组容器。int[] arr = new int[]{1,2,3,4,5};//正确 //或 int[] arr; arr = new int[]{1,2,3,4,5};//正确一维数组声明和静态初始化格式2:数据类型[] 数组名 = {元素1,元素2,元素3...};//必须在一个语句中完成,不能分成两个语句写例如,定义存储1,2,3,4,5整数的数组容器int[] arr = {1,2,3,4,5};//正确 int[] arr; arr = {1,2,3,4,5};//错误举例:public class ArrayTest2 { public static void main(String[] args) { int[] arr = {1,2,3,4,5};//右边不需要写new int[] int[] nums; nums = new int[]{10,20,30,40}; //声明和初始化在两个语句完成,就不能使用new int[] char[] word = {'h','e','l','l','o'}; String[] heros = {"袁隆平","邓稼先","钱学森"}; System.out.println("arr数组:" + arr);//arr数组:[I@1b6d3586 System.out.println("nums数组:" + nums);//nums数组:[I@4554617c System.out.println("word数组:" + word);//word数组:[C@74a14482 System.out.println("heros数组:" + heros);//heros数组:[Ljava.lang.String;@1540e19d } }动态初始化数组变量的初始化和数组元素的赋值操作分开进行,即为动态初始化。动态初始化中,只确定了元素的个数(即数组的长度),而元素值此时只是默认值,还并未真正赋自己期望的值。真正期望的数据需要后续单独一个一个赋值。格式:数组存储的元素的数据类型[] 数组名字 = new 数组存储的元素的数据类型[长度]; 或 数组存储的数据类型[] 数组名字; 数组名字 = new 数组存储的数据类型[长度];[长度]:数组的长度,表示数组容器中可以最多存储多少个元素。注意:数组有定长特性,长度一旦指定,不可更改。和水杯道理相同,买了一个2升的水杯,总容量就是2升是固定的。举例1:正确写法int[] arr = new int[5]; int[] arr; arr = new int[5]; 举例2:错误写法int[] arr = new int[5]{1,2,3,4,5};//错误的,后面有{}指定元素列表,就不需要在[]中指定元素个数了。一维数组的使用数组的长度数组的元素总个数,即数组的长度每个数组都有一个属性length指明它的长度,例如:arr.length 指明数组arr的长度(即元素个数)每个数组都具有长度,而且一旦初始化,其长度就是确定,且是不可变的。数组元素的引用如何表示数组中的一个元素?每一个存储到数组的元素,都会自动的拥有一个编号,从0开始,这个自动编号称为数组索引(index)或下标,可以通过数组的索引/下标访问到数组中的元素。数组名[索引/下标]数组的下标范围?Java中数组的下标从[0]开始,下标范围是[0, 数组的长度-1],即[0, 数组名.length-1]数组元素下标可以是整型常量或整型表达式。如a[3] , b[i] , c[6*i];举例public class ArrayTest3 { public static void main(String[] args) { int[] arr = {1,2,3,4,5}; System.out.println("arr数组的长度:" + arr.length); System.out.println("arr数组的第1个元素:" + arr[0]);//下标从0开始 System.out.println("arr数组的第2个元素:" + arr[1]); System.out.println("arr数组的第3个元素:" + arr[2]); System.out.println("arr数组的第4个元素:" + arr[3]); System.out.println("arr数组的第5个元素:" + arr[4]); //修改第1个元素的值 //此处arr[0]相当于一个int类型的变量 arr[0] = 100; System.out.println("arr数组的第1个元素:" + arr[0]); } }一维数组的遍历将数组中的每个元素分别获取出来,就是遍历。for循环与数组的遍历是绝配。举例1public class ArrayTest4 { public static void main(String[] args) { int[] arr = new int[]{1,2,3,4,5}; //打印数组的属性,输出结果是5 System.out.println("数组的长度:" + arr.length); //遍历输出数组中的元素 System.out.println("数组的元素有:"); for(int i=0; i<arr.length; i++){ System.out.println(arr[i]); } } }举例2public class ArrayTest5 { public static void main(String[] args) { int[] arr = new int[5]; System.out.println("arr数组的长度:" + arr.length); System.out.print("存储数据到arr数组之前:["); for (int i = 0; i < arr.length; i++) { if(i==0){ System.out.print(arr[i]); }else{ System.out.print("," + arr[i]); } } System.out.println("]"); //初始化 /* arr[0] = 2; arr[1] = 4; arr[2] = 6; arr[3] = 8; arr[4] = 10; */ for (int i = 0; i < arr.length; i++) { arr[i] = (i+1) * 2; } System.out.print("存储数据到arr数组之后:["); for (int i = 0; i < arr.length; i++) { if(i==0){ System.out.print(arr[i]); }else{ System.out.print("," + arr[i]); } } System.out.println("]"); } }数组元素的默认值数组是引用类型,当我们使用动态初始化方式创建数组时,元素值只是默认值。例如:public class ArrayTest6 { public static void main(String argv[]){ int a[]= new int[5]; System.out.println(a[3]); //a[3]的默认值为0 } } 对于基本数据类型而言,默认初始化值各有不同。对于引用数据类型而言,默认初始化值为null(注意与0不同!)public class ArrayTest7 { public static void main(String[] args) { //存储26个字母 char[] letters = new char[26]; System.out.println("letters数组的长度:" + letters.length); System.out.print("存储字母到letters数组之前:["); for (int i = 0; i < letters.length; i++) { if(i==0){ System.out.print(letters[i]); }else{ System.out.print("," + letters[i]); } } System.out.println("]"); //存储5个姓名 String[] names = new String[5]; System.out.println("names数组的长度:" + names.length); System.out.print("存储姓名到names数组之前:["); for (int i = 0; i < names.length; i++) { if(i==0){ System.out.print(names[i]); }else{ System.out.print("," + names[i]); } } System.out.println("]"); } }一维数组内存分析Java虚拟机的内存划分为了提高运算效率,就对空间进行了不同区域的划分,因为每一片区域都有特定的处理数据方式和内存管理方式。区域名称作用虚拟机栈用于存储正在执行的每个Java方法的局部变量表等。局部变量表存放了编译期可知长度<br/>的各种基本数据类型、对象引用,方法执行完,自动释放。堆内存存储对象(包括数组对象),new来创建的,都存储在堆内存。方法区存储已被虚拟机加载的类信息、常量、(静态变量)、即时编译器编译后的代码等数据。本地方法栈当程序中调用了native的本地方法时,本地方法执行期间的内存区域程序计数器程序计数器是CPU中的寄存器,它包含每一个线程下一条要执行的指令的地址一维数组在内存中的存储1、一个一维数组内存图public static void main(String[] args) { int[] arr = new int[3]; System.out.println(arr);//[I@5f150435 } 2、数组下标为什么是0开始因为第一个元素距离数组首地址间隔0个单元格。3、两个一维数组内存图两个数组独立public static void main(String[] args) { int[] arr = new int[3]; int[] arr2 = new int[2]; System.out.println(arr); System.out.println(arr2); } 4、两个变量指向一个一维数组两个数组变量本质上代表同一个数组。public static void main(String[] args) { // 定义数组,存储3个元素 int[] arr = new int[3]; //数组索引进行赋值 arr[0] = 5; arr[1] = 6; arr[2] = 7; //输出3个索引上的元素值 System.out.println(arr[0]); System.out.println(arr[1]); System.out.println(arr[2]); //定义数组变量arr2,将arr的地址赋值给arr2 int[] arr2 = arr; arr2[1] = 9; System.out.println(arr[1]); }4. 一维数组的应用案例1:升景坊单间短期出租4个月,550元/月(水电煤公摊,网费35元/月),空调、卫生间、厨房齐全。屋内均是IT行业人士,喜欢安静。所以要求来租者最好是同行或者刚毕业的年轻人,爱干净、安静。public class ArrayTest { public static void main(String[] args) { int[] arr = new int[]{8,2,1,0,3}; int[] index = new int[]{2,0,3,2,4,0,1,3,2,3,3}; String tel = ""; for(int i = 0;i < index.length;i++){ tel += arr[index[i]]; } System.out.println("联系方式:" + tel); } } 案例2:输出英文星期几用一个数组,保存星期一到星期天的7个英语单词,从键盘输入1-7,显示对应的单词{"Monday","Tuesday","Wednesday","Thursday","Friday","Saturday","Sunday"}import java.util.Scanner; public class WeekArrayTest { public static void main(String[] args) { //1. 声明并初始化星期的数组 String[] weeks = {"Monday","Tuesday","Wednesday","Thursday","Friday","Saturday","Sunday"}; //2. 使用Scanner从键盘获取1-7范围的整数 Scanner scanner = new Scanner(System.in); System.out.println("请输入[1-7]范围的整数:"); int number = scanner.nextInt(); if(number < 1 || number > 7){ System.out.println("你输入的输入非法"); }else{ //3. 根据输入的整数,到数组中相应的索引位置获取指定的元素(即:星期几) System.out.println("对应的星期为:" + weeks[number - 1]); } scanner.close(); } }案例3:从键盘读入学生成绩,找出最高分,并输出学生成绩等级。成绩>=最高分-10 等级为’A’成绩>=最高分-20 等级为’B’成绩>=最高分-30 等级为’C’其余 等级为’D’提示:先读入学生人数,根据人数创建int数组,存放学生成绩。 public class ScoreTest1 { public static void main(String[] args) { //1. 根据提示,获取学生人数 System.out.print("请输入学生人数:"); Scanner scanner = new Scanner(System.in); int count = scanner.nextInt(); //2. 根据学生人数,创建指定长度的数组 (使用动态初始化) int[] scores = new int[count]; //3. 使用循环,依次给数组的元素赋值 int maxScore = 0; //记录最高分 System.out.println("请输入" + count + "个成绩"); for (int i = 0; i < scores.length; i++) { scores[i] = scanner.nextInt(); //4. 获取数组中元素的最大值,即为最高分 if(maxScore < scores[i]){ maxScore = scores[i]; } } System.out.println("最高分是:" + maxScore); //5. 遍历数组元素,输出各自的分数,并根据其分数与最高分的差值,获取各自的等级 char grade; for (int i = 0; i < scores.length; i++) { if(scores[i] >= maxScore - 10){ grade = 'A'; }else if(scores[i] >= maxScore - 20){ grade = 'B'; }else if(scores[i] >= maxScore - 30){ grade = 'C'; }else{ grade = 'D'; } System.out.println("student " + i + " socre is " + scores[i] + ", grade is " + grade); } //关闭资源 scanner.close(); } }数组的常见算法数值型数组特征值统计这里的特征值涉及到:平均值、最大值、最小值、总和等举例1:数组统计:求总和、均值public class TestArrayElementSum { public static void main(String[] args) { int[] arr = {4,5,6,1,9}; //求总和、均值 int sum = 0;//因为0加上任何数都不影响结果 for(int i=0; i<arr.length; i++){ sum += arr[i]; } double avg = (double)sum/arr.length; System.out.println("sum = " + sum); System.out.println("avg = " + avg); } }举例2:求数组元素的总乘积public class TestArrayElementMul { public static void main(String[] args) { int[] arr = {4,5,6,1,9}; //求总乘积 long result = 1;//因为1乘以任何数都不影响结果 for(int i=0; i<arr.length; i++){ result *= arr[i]; } System.out.println("result = " + result); } }举例3:求数组元素中偶数的个数public class TestArrayElementEvenCount { public static void main(String[] args) { int[] arr = {4,5,6,1,9}; //统计偶数个数 int evenCount = 0; for(int i=0; i<arr.length; i++){ if(arr[i]%2==0){ evenCount++; } } System.out.println("evenCount = " + evenCount); } }举例4:求数组元素的最大值public class TestArrayMax { public static void main(String[] args) { int[] arr = {4,5,6,1,9}; //找最大值 int max = arr[0]; for(int i=1; i<arr.length; i++){//此处i从1开始,是max不需要与arr[0]再比较一次了 if(arr[i] > max){ max = arr[i]; } } System.out.println("max = " + max); } }举例5:找最值及其第一次出现的下标public class TestMaxIndex { public static void main(String[] args) { int[] arr = {4,5,6,1,9}; //找最大值以及第一个最大值下标 int max = arr[0]; int index = 0; for(int i=1; i<arr.length; i++){ if(arr[i] > max){ max = arr[i]; index = i; } } System.out.println("max = " + max); System.out.println("index = " + index); } }举例6:找最值及其所有最值的下标public class Test13AllMaxIndex { public static void main(String[] args) { int[] arr = {4,5,6,1,9,9,3}; //找最大值 int max = arr[0]; for(int i=1; i<arr.length; i++){ if(arr[i] > max){ max = arr[i]; } } System.out.println("最大值是:" + max); System.out.print("最大值的下标有:"); //遍历数组,看哪些元素和最大值是一样的 for(int i=0; i<arr.length; i++){ if(max == arr[i]){ System.out.print(i+"\t"); } } System.out.println(); } }优化public class Test13AllMaxIndex2 { public static void main(String[] args) { int[] arr = {4,5,6,1,9,9,3}; //找最大值 int max = arr[0]; String index = "0"; for(int i=1; i<arr.length; i++){ if(arr[i] > max){ max = arr[i]; index = i + ""; }else if(arr[i] == max){ index += "," + i; } } System.out.println("最大值是" + max); System.out.println("最大值的下标是[" + index+"]"); } }举例7(难):输入一个整形数组,数组里有正数也有负数。数组中连续的一个或多个整数组成一个子数组,每个子数组都有一个和。求所有子数组的和的最大值。要求时间复杂度为O(n)。例如:输入的数组为1, -2, 3, -10, -4, 7, 2, -5,和最大的子数组为3, 10, -4, 7, 2,因此输出为该子数组的和18。public class Test5 { public static void main(String[] args) { int[] arr = new int[]{1, -2, 3, 10, -4, 7, 2, -5}; int i = getGreatestSum(arr); System.out.println(i); } public static int getGreatestSum(int[] arr){ int greatestSum = 0; if(arr == null || arr.length == 0){ return 0; } int temp = greatestSum; for(int i = 0;i < arr.length;i++){ temp += arr[i]; if(temp < 0){ temp = 0; } if(temp > greatestSum){ greatestSum = temp; } } if(greatestSum == 0){ greatestSum = arr[0]; for(int i = 1;i < arr.length;i++){ if(greatestSum < arr[i]){ greatestSum = arr[i]; } } } return greatestSum; } }举例8:评委打分分析以下需求,并用代码实现:(1)在编程竞赛中,有10位评委为参赛的选手打分,分数分别为:5,4,6,8,9,0,1,2,7,3(2)求选手的最后得分(去掉一个最高分和一个最低分后其余8位评委打分的平均值) public class ArrayExer { public static void main(String[] args) { int[] scores = {5,4,6,8,9,0,1,2,7,3}; int max = scores[0]; int min = scores[0]; int sum = 0; for(int i = 0;i < scores.length;i++){ if(max < scores[i]){ max = scores[i]; } if(min > scores[i]){ min = scores[i]; } sum += scores[i]; } double avg = (double)(sum - max - min) / (scores.length - 2); System.out.println("选手去掉最高分和最低分之后的平均分为:" + avg); } }数组元素的赋值与数组复制举例1:杨辉三角(见二维数组课后案例)举例2:使用简单数组(1)创建一个名为ArrayTest的类,在main()方法中声明array1和array2两个变量,他们是int[]类型的数组。(2)使用大括号{},把array1初始化为8个素数:2,3,5,7,11,13,17,19。(3)显示array1的内容。(4)赋值array2变量等于array1,修改array2中的偶索引元素,使其等于索引值(如array[0]=0,array[2]=2)。打印出array1。 array2 = array1;思考:array1和array2是什么关系?拓展:修改题目,实现array2对array1数组的复制举例3:一个数组,让数组的每个元素去除第一个元素,得到的商作为被除数所在位置的新值。public class Test3 { public static void main(String[] args) { int[] arr = new int[]{12,43,65,3,-8,64,2}; // for(int i = 0;i < arr.length;i++){ // arr[i] = arr[i] / arr[0]; // } for(int i = arr.length -1;i >= 0;i--){ arr[i] = arr[i] / arr[0]; } //遍历arr for(int i = 0;i < arr.length;i++){ System.out.print(arr[i] + " "); } } }举例4:创建一个长度为6的int型数组,要求数组元素的值都在1-30之间,且是随机赋值。同时,要求元素的值各不相同。public class Test4 { // 5-67 Math.random() * 63 + 5; @Test public void test1() { int[] arr = new int[6]; for (int i = 0; i < arr.length; i++) {// [0,1) [0,30) [1,31) arr[i] = (int) (Math.random() * 30) + 1; boolean flag = false; while (true) { for (int j = 0; j < i; j++) { if (arr[i] == arr[j]) { flag = true; break; } } if (flag) { arr[i] = (int) (Math.random() * 30) + 1; flag = false; continue; } break; } } for (int i = 0; i < arr.length; i++) { System.out.println(arr[i]); } } //更优的方法 @Test public void test2(){ int[] arr = new int[6]; for (int i = 0; i < arr.length; i++) {// [0,1) [0,30) [1,31) arr[i] = (int) (Math.random() * 30) + 1; for (int j = 0; j < i; j++) { if (arr[i] == arr[j]) { i--; break; } } } for (int i = 0; i < arr.length; i++) { System.out.println(arr[i]); } } }举例5:扑克牌案例:遍历扑克牌遍历扑克牌,效果如图所示:提示:使用两个字符串数组,分别保存花色和点数,再用一个字符串数组保存最后的扑克牌。String[] hua = {"黑桃","红桃","梅花","方片"};String[] dian = {"A","2","3","4","5","6","7","8","9","10","J","Q","K"}; public class ArrayExer05 { public static void main(String[] args) { String[] hua = {"黑桃","红桃","梅花","方片"}; String[] dian = {"A","2","3","4","5","6","7","8","9","10","J","Q","K"}; String[] pai = new String[hua.length * dian.length]; int k = 0; for(int i = 0;i < hua.length;i++){ for(int j = 0;j < dian.length;j++){ pai[k++] = hua[i] + dian[j]; } } for (int i = 0; i < pai.length; i++) { System.out.print(pai[i] + " "); if(i % 13 == 12){ System.out.println(); } } } } 拓展:在上述基础上,增加大王、小王。举例6:回形数从键盘输入一个整数(1~20) ,则以该数字为矩阵的大小,把1,2,3…n*n 的数字按照顺时针螺旋的形式填入其中。例如: 输入数字2,则程序输出: 1 2 4 3 输入数字3,则程序输出: 1 2 3 8 9 4 7 6 5 输入数字4, 则程序输出: 1 2 3 4 12 13 14 5 11 16 15 6 10 9 8 7//方式1 public class RectangleTest { public static void main(String[] args) { Scanner scanner = new Scanner(System.in); System.out.println("输入一个数字"); int len = scanner.nextInt(); int[][] arr = new int[len][len]; int s = len * len; /* * k = 1:向右 * k = 2:向下 * k = 3:向左 * k = 4:向上 */ int k = 1; int i = 0,j = 0; for(int m = 1;m <= s;m++){ if(k == 1){ if(j < len && arr[i][j] == 0){ arr[i][j++] = m; }else{ k = 2; i++; j--; m--; } }else if(k == 2){ if(i < len && arr[i][j] == 0){ arr[i++][j] = m; }else{ k = 3; i--; j--; m--; } }else if(k == 3){ if(j >= 0 && arr[i][j] == 0){ arr[i][j--] = m; }else{ k = 4; i--; j++; m--; } }else if(k == 4){ if(i >= 0 && arr[i][j] == 0){ arr[i--][j] = m; }else{ k = 1; i++; j++; m--; } } } //遍历 for(int m = 0;m < arr.length;m++){ for(int n = 0;n < arr[m].length;n++){ System.out.print(arr[m][n] + "\t"); } System.out.println(); } } }//方式2 /* 01 02 03 04 05 06 07 24 25 26 27 28 29 08 23 40 41 42 43 30 09 22 39 48 49 44 31 10 21 38 47 46 45 32 11 20 37 36 35 34 33 12 19 18 17 16 15 14 13 */ public class RectangleTest1 { public static void main(String[] args) { int n = 7; int[][] arr = new int[n][n]; int count = 0; //要显示的数据 int maxX = n-1; //x轴的最大下标 int maxY = n-1; //Y轴的最大下标 int minX = 0; //x轴的最小下标 int minY = 0; //Y轴的最小下标 while(minX<=maxX) { for(int x=minX;x<=maxX;x++) { arr[minY][x] = ++count; } minY++; for(int y=minY;y<=maxY;y++) { arr[y][maxX] = ++count; } maxX--; for(int x=maxX;x>=minX;x--) { arr[maxY][x] = ++count; } maxY--; for(int y=maxY;y>=minY;y--) { arr[y][minX] = ++count; } minX++; } for(int i=0;i<arr.length;i++) { for(int j=0;j<arr.length;j++) { String space = (arr[i][j]+"").length()==1 ? "0":""; System.out.print(space+arr[i][j]+" "); } System.out.println(); } } }数组元素的反转实现思想:数组对称位置的元素互换。public class TestArrayReverse1 { public static void main(String[] args) { int[] arr = {1,2,3,4,5}; System.out.println("反转之前:"); for (int i = 0; i < arr.length; i++) { System.out.println(arr[i]); } //反转 /* 思路:首尾对应位置的元素交换 (1)确定交换几次 次数 = 数组.length / 2 (2)谁和谁交换 for(int i=0; i<次数; i++){ int temp = arr[i]; arr[i] = arr[arr.length-1-i]; arr[arr.length-1-i] = temp; } */ for(int i=0; i<arr.length/2; i++){ int temp = arr[i]; arr[i] = arr[arr.length-1-i]; arr[arr.length-1-i] = temp; } System.out.println("反转之后:"); for (int i = 0; i < arr.length; i++) { System.out.println(arr[i]); } } }或public class TestArrayReverse2 { public static void main(String[] args) { int[] arr = {1,2,3,4,5}; System.out.println("反转之前:"); for (int i = 0; i < arr.length; i++) { System.out.println(arr[i]); } //反转 //左右对称位置交换 for(int left=0,right=arr.length-1; left<right; left++,right--){ //首 与 尾交换 int temp = arr[left]; arr[left] = arr[right]; arr[right] = temp; } System.out.println("反转之后:"); for (int i = 0; i < arr.length; i++) { System.out.println(arr[i]); } } }数组的扩容与缩容数组的扩容题目:现有数组 int[] arr = new int[]{1,2,3,4,5}; ,现将数组长度扩容1倍,并将10,20,30三个数据添加到arr数组中,如何操作?public class ArrTest1 { public static void main(String[] args) { int[] arr = new int[]{1,2,3,4,5}; int[] newArr = new int[arr.length << 1]; for(int i = 0;i < arr.length;i++){ newArr[i] = arr[i]; } newArr[arr.length] = 10; newArr[arr.length + 1] = 20; newArr[arr.length + 2] = 30; arr = newArr; //遍历arr for (int i = 0; i < arr.length; i++) { System.out.println(arr[i]); } } }数组的缩容题目:现有数组 int[] arr={1,2,3,4,5,6,7}。现需删除数组中索引为4的元素。public class ArrTest2 { public static void main(String[] args) { int[] arr = {1, 2, 3, 4, 5, 6, 7}; //删除数组中索引为4的元素 int delIndex = 4; //方案1: /*//创建新数组 int[] newArr = new int[arr.length - 1]; for (int i = 0; i < delIndex; i++) { newArr[i] = arr[i]; } for (int i = delIndex + 1; i < arr.length; i++) { newArr[i - 1] = arr[i]; } arr = newArr; for (int i = 0; i < arr.length; i++) { System.out.println(arr[i]); }*/ //方案2: for (int i = delIndex; i < arr.length - 1; i++) { arr[i] = arr[i + 1]; } arr[arr.length - 1] = 0; for (int i = 0; i < arr.length; i++) { System.out.println(arr[i]); } } }数组的元素查找1、顺序查找顺序查找:挨个查看要求:对数组元素的顺序没要求public class TestArrayOrderSearch { //查找value第一次在数组中出现的index public static void main(String[] args){ int[] arr = {4,5,6,1,9}; int value = 1; int index = -1; for(int i=0; i<arr.length; i++){ if(arr[i] == value){ index = i; break; } } if(index==-1){ System.out.println(value + "不存在"); }else{ System.out.println(value + "的下标是" + index); } } } 二分查找实现步骤://二分法查找:要求此数组必须是有序的。 int[] arr3 = new int[]{-99,-54,-2,0,2,33,43,256,999}; boolean isFlag = true; int value = 256; //int value = 25; int head = 0;//首索引位置 int end = arr3.length - 1;//尾索引位置 while(head <= end){ int middle = (head + end) / 2; if(arr3[middle] == value){ System.out.println("找到指定的元素,索引为:" + middle); isFlag = false; break; }else if(arr3[middle] > value){ end = middle - 1; }else{//arr3[middle] < value head = middle + 1; } } if(isFlag){ System.out.println("未找打指定的元素"); } 数组元素排序算法概述定义排序:排序是计算机内经常进行的一种操作,其目的是将一组“无序”的记录序列调整为“有序”的记录序列。通常来说,排序的目的是快速查找。衡量排序算法的优劣:时间复杂度:分析关键字的比较次数和记录的移动次数常见的算法时间复杂度由小到大依次为:Ο(1)<Ο(log2n)<Ο(n)<Ο(nlog2n)<Ο(n2)<Ο(n3)<…<Ο(2n)<Ο(n!)<O(nn)空间复杂度:分析排序算法中需要多少辅助内存一个算法的空间复杂度S(n)定义为该算法所耗费的存储空间,它也是问题规模n的函数。稳定性:若两个记录A和B的关键字值相等,但排序后A、B的先后次序保持不变,则称这种排序算法是稳定的。排序算法概述排序算法分类:内部排序和外部排序内部排序:整个排序过程不需要借助于外部存储器(如磁盘等),所有排序操作都在内存中完成。外部排序:参与排序的数据非常多,数据量非常大,计算机无法把整个排序过程放在内存中完成,必须借助于外部存储器(如磁盘)。外部排序最常见的是多路归并排序。可以认为外部排序是由多次内部排序组成。十大内部排序算法 数组的排序算法很多,实现方式各不相同,时间复杂度、空间复杂度、稳定性也各不相同:常见时间复杂度所消耗的时间从小到大排序:O(1) < O(logn) < O(n) < O(nlogn) < O(n^2) < O(n^3) < O(2^n) < O(n!) < O(n^n)注意,经常将以2为底n的对数简写成logn。冒泡排序(Bubble Sort)排序思想:比较相邻的元素。如果第一个比第二个大(升序),就交换他们两个。对每一对相邻元素作同样的工作,从开始第一对到结尾的最后一对。这步做完后,最后的元素会是最大的数。针对所有的元素重复以上的步骤,除了最后一个。持续每次对越来越少的元素重复上面的步骤,直到没有任何一对数字需要比较为止。/* 1、冒泡排序(最经典) 思想:每一次比较“相邻(位置相邻)”元素,如果它们不符合目标顺序(例如:从小到大), 就交换它们,经过多轮比较,最终实现排序。 (例如:从小到大) 每一轮可以把最大的沉底,或最小的冒顶。 过程:arr{6,9,2,9,1} 目标:从小到大 第一轮: 第1次,arr[0]与arr[1],6>9不成立,满足目标要求,不交换 第2次,arr[1]与arr[2],9>2成立,不满足目标要求,交换arr[1]与arr[2] {6,2,9,9,1} 第3次,arr[2]与arr[3],9>9不成立,满足目标要求,不交换 第4次,arr[3]与arr[4],9>1成立,不满足目标要求,交换arr[3]与arr[4] {6,2,9,1,9} 第一轮所有元素{6,9,2,9,1}已经都参与了比较,结束。 第一轮的结果:第“一”最大值9沉底(本次是后面的9沉底),即到{6,2,9,1,9}元素的最右边 第二轮: 第1次,arr[0]与arr[1],6>2成立,不满足目标要求,交换arr[0]与arr[1] {2,6,9,1,9} 第2次,arr[1]与arr[2],6>9不成立,满足目标要求,不交换 第3次:arr[2]与arr[3],9>1成立,不满足目标要求,交换arr[2]与arr[3] {2,6,1,9,9} 第二轮未排序的所有元素 {6,2,9,1}已经都参与了比较,结束。 第二轮的结果:第“二”最大值9沉底(本次是前面的9沉底),即到{2,6,1,9}元素的最右边 第三轮: 第1次,arr[0]与arr[1],2>6不成立,满足目标要求,不交换 第2次,arr[1]与arr[2],6>1成立,不满足目标要求,交换arr[1]与arr[2] {2,1,6,9,9} 第三轮未排序的所有元素{2,6,1}已经都参与了比较,结束。 第三轮的结果:第三最大值6沉底,即到 {2,1,6}元素的最右边 第四轮: 第1次,arr[0]与arr[1],2>1成立,不满足目标要求,交换arr[0]与arr[1] {1,2,6,9,9} 第四轮未排序的所有元素{2,1}已经都参与了比较,结束。 第四轮的结果:第四最大值2沉底,即到{1,2}元素的最右边 */ public class Test19BubbleSort{ public static void main(String[] args){ int[] arr = {6,9,2,9,1}; //目标:从小到大 //冒泡排序的轮数 = 元素的总个数 - 1 //轮数是多轮,每一轮比较的次数是多次,需要用到双重循环,即循环嵌套 //外循环控制 轮数,内循环控制每一轮的比较次数和过程 for(int i=1; i<arr.length; i++){ //循环次数是arr.length-1次/轮 /* 假设arr.length=5 i=1,第1轮,比较4次 arr[0]与arr[1] arr[1]与arr[2] arr[2]与arr[3] arr[3]与arr[4] arr[j]与arr[j+1],int j=0;j<4; j++ i=2,第2轮,比较3次 arr[0]与arr[1] arr[1]与arr[2] arr[2]与arr[3] arr[j]与arr[j+1],int j=0;j<3; j++ i=3,第3轮,比较2次 arr[0]与arr[1] arr[1]与arr[2] arr[j]与arr[j+1],int j=0;j<2; j++ i=4,第4轮,比较1次 arr[0]与arr[1] arr[j]与arr[j+1],int j=0;j<1; j++ int j=0; j<arr.length-i; j++ */ for(int j=0; j<arr.length-i; j++){ //希望的是arr[j] < arr[j+1] if(arr[j] > arr[j+1]){ //交换arr[j]与arr[j+1] int temp = arr[j]; arr[j] = arr[j+1]; arr[j+1] = temp; } } } //完成排序,遍历结果 for(int i=0; i<arr.length; i++){ System.out.print(arr[i]+" "); } } }冒泡排序优化(选讲)/* 思考:冒泡排序是否可以优化 */ class Test19BubbleSort2{ public static void main(String[] args) { int[] arr = {1, 3, 5, 7, 9}; //从小到大排序 for (int i = 0; i < arr.length - 1; i++) { boolean flag = true;//假设数组已经是有序的 for (int j = 0; j < arr.length - 1 - i; j++) { //希望的是arr[j] < arr[j+1] if (arr[j] > arr[j + 1]) { //交换arr[j]与arr[j+1] int temp = arr[j]; arr[j] = arr[j + 1]; arr[j + 1] = temp; flag = false;//如果元素发生了交换,那么说明数组还没有排好序 } } if (flag) { break; } } //完成排序,遍历结果 for (int i = 0; i < arr.length; i++) { System.out.print(arr[i] + " "); } } }数组中的常见异常数组角标越界异常当访问数组元素时,下标指定超出[0, 数组名.length-1]的范围时,就会报数组下标越界异常:ArrayIndexOutOfBoundsException。public class TestArrayIndexOutOfBoundsException { public static void main(String[] args) { int[] arr = {1,2,3}; // System.out.println("最后一个元素:" + arr[3]);//错误,下标越界 // System.out.println("最后一个元素:" + arr[arr.length]);//错误,下标越界 System.out.println("最后一个元素:" + arr[arr.length-1]);//对 } } 创建数组,赋值3个元素,数组的索引就是0,1,2,没有3索引,因此我们不能访问数组中不存在的索引,程序运行后,将会抛出 ArrayIndexOutOfBoundsException 数组越界异常。在开发中,数组的越界异常是不能出现的,一旦出现了,就必须要修改我们编写的代码。空指针异常观察一下代码,运行后会出现什么结果。public class TestNullPointerException { public static void main(String[] args) { //定义数组 int[][] arr = new int[3][]; System.out.println(arr[0][0]);//NullPointerException } }因为此时数组的每一行还未分配具体存储元素的空间,此时arr[0]是null,此时访问arr[0][0]会抛出NullPointerException 空指针异常。小结:空指针异常情况 //举例一: // int[] arr1 = new int[10]; // arr1 = null; // System.out.println(arr1[9]); //举例二: // int[][] arr2 = new int[5][]; // //arr2[3] = new int[10]; // System.out.println(arr2[3][3]); //举例三: String[] arr3 = new String[10]; System.out.println(arr3[2].toString());多维数组的使用概述Java 语言里提供了支持多维数组的语法。分类二维数组:所谓二维数组其实就是多个一维数组作为元素,存储在一个一维数组中三维数组所谓三维数组其实就是多个二维数组作为元素,存储在一个一维数组中四维数组所谓三维数组其实就是多个三维数组作为元素,存储在一个一维数组中......声明与初始化声明二维数组声明的语法格式://推荐 元素的数据类型[][] 二维数组的名; //不推荐 元素的数据类型 二维数组名[][]; //不推荐 元素的数据类型[] 二维数组名[];例如:public class Test20TwoDimensionalArrayDefine { public static void main(String[] args) { //存储多组成绩 int[][] grades; //存储多组姓名 String[][] names; } }面试:int[] x, y[]; //x是一维数组,y是二维数组7.2.2 静态初始化格式:int[][] arr = new int[][]{{3,8,2},{2,7},{9,0,1,6}};定义一个名称为arr的二维数组,二维数组中有三个一维数组每一个一维数组中具体元素也都已初始化第一个一维数组 arr[0] = {3,8,2};第二个一维数组 arr[1] = {2,7};第三个一维数组 arr[2] = {9,0,1,6};第三个一维数组的长度表示方式:arr[2].length;注意特殊写法情况:int[] x,y[]; x是一维数组,y是二维数组。举例1:int[][] arr = {{1,2,3},{4,5,6},{7,8,9,10}};//声明与初始化必须在一句完成 int[][] arr = new int[][]{{1,2,3},{4,5,6},{7,8,9,10}}; int[][] arr; arr = new int[][]{{1,2,3},{4,5,6},{7,8,9,10}}; arr = new int[3][3]{{1,2,3},{4,5,6},{7,8,9,10}};//错误,静态初始化右边new 数据类型[][]中不能写数字举例2:public class TwoDimensionalArrayInitialize { public static void main(String[] args) { //存储多组成绩 int[][] grades = { {89,75,99,100}, {88,96,78,63,100,86}, {56,63,58}, {99,66,77,88} }; //存储多组姓名 String[][] names = { {"张三","李四", "王五", "赵六"}, {"刘备","关羽","张飞","诸葛亮","赵云","马超"}, {"曹丕","曹植","曹冲"}, {"孙权","周瑜","鲁肃","黄盖"} }; } }动态初始化如果二维数组的每一个数据,甚至是每一行的列数,需要后期单独确定,那么就只能使用动态初始化方式了。动态初始化方式分为两种格式:格式1:规则二维表:每一行的列数是相同的//(1)确定行数和列数 元素的数据类型[][] 二维数组名 = new 元素的数据类型[m][n]; //其中,m:表示这个二维数组有多少个一维数组。或者说一共二维表有几行 //其中,n:表示每一个一维数组的元素有多少个。或者说每一行共有一个单元格 //此时创建完数组,行数、列数确定,而且元素也都有默认值 //(2)再为元素赋新值 二维数组名[行下标][列下标] = 值;举例:int[][] arr = new int[3][2];定义了名称为arr的二维数组二维数组中有3个一维数组每一个一维数组中有2个元素一维数组的名称分别为arr[0], arr[1], arr[2]给第一个一维数组1脚标位赋值为78写法是:arr[0][1] = 78;格式2:不规则:每一行的列数不一样//(1)先确定总行数 元素的数据类型[][] 二维数组名 = new 元素的数据类型[总行数][]; //此时只是确定了总行数,每一行里面现在是null //(2)再确定每一行的列数,创建每一行的一维数组 二维数组名[行下标] = new 元素的数据类型[该行的总列数]; //此时已经new完的行的元素就有默认值了,没有new的行还是null //(3)再为元素赋值 二维数组名[行下标][列下标] = 值;举例:int[][] arr = new int[3][];二维数组中有3个一维数组。每个一维数组都是默认初始化值null (注意:区别于格式1)可以对这个三个一维数组分别进行初始化:arr[0] = new int[3]; arr[1] = new int[1]; arr[2] = new int[2];注:int[][]arr = new int[][3]; //非法练习:/* 1 2 2 3 3 3 4 4 4 4 5 5 5 5 5 */ public class Test25DifferentElementCount { public static void main(String[] args){ //1、声明一个二维数组,并且确定行数 //因为每一行的列数不同,这里无法直接确定列数 int[][] arr = new int[5][]; //2、确定每一行的列数 for(int i=0; i<arr.length; i++){ /* arr[0] 的列数是1 arr[1] 的列数是2 arr[2] 的列数是3 arr[3] 的列数是4 arr[4] 的列数是5 */ arr[i] = new int[i+1]; } //3、确定元素的值 for(int i=0; i<arr.length; i++){ for(int j=0; j<arr[i].length; j++){ arr[i][j] = i+1; } } //4、遍历显示 for(int i=0; i<arr.length; i++){ for(int j=0; j<arr[i].length; j++){ System.out.print(arr[i][j] + " "); } System.out.println(); } } }数组的长度和角标二维数组的长度/行数:二维数组名.length二维数组的某一行:二维数组名[行下标],此时相当于获取其中一组数据。它本质上是一个一维数组。行下标的范围:[0, 二维数组名.length-1]。此时把二维数组看成一维数组的话,元素是行对象。某一行的列数:二维数组名[行下标].length,因为二维数组的每一行是一个一维数组。某一个元素:二维数组名[行下标][列下标],即先确定行/组,再确定列。public class Test22TwoDimensionalArrayUse { public static void main(String[] args){ //存储3个小组的学员的成绩,分开存储,使用二维数组。 /* int[][] scores1; int scores2[][]; int[] scores3[];*/ int[][] scores = { {85,96,85,75}, {99,96,74,72,75}, {52,42,56,75} }; System.out.println(scores);//[[I@15db9742 System.out.println("一共有" + scores.length +"组成绩."); //[[:代表二维数组,I代表元素类型是int System.out.println(scores[0]);//[I@6d06d69c //[:代表一维数组,I代表元素类型是int System.out.println(scores[1]);//[I@7852e922 System.out.println(scores[2]);//[I@4e25154f //System.out.println(scores[3]);//ArrayIndexOutOfBoundsException: 3 System.out.println("第1组有" + scores[0].length +"个学员."); System.out.println("第2组有" + scores[1].length +"个学员."); System.out.println("第3组有" + scores[2].length +"个学员."); System.out.println("第1组的每一个学员成绩如下:"); //第一行的元素 System.out.println(scores[0][0]);//85 System.out.println(scores[0][1]);//96 System.out.println(scores[0][2]);//85 System.out.println(scores[0][3]);//75 //System.out.println(scores[0][4]);//java.lang.ArrayIndexOutOfBoundsException: 4 } }二维数组的遍历格式:for(int i=0; i<二维数组名.length; i++){ //二维数组对象.length for(int j=0; j<二维数组名[i].length; j++){//二维数组行对象.length System.out.print(二维数组名[i][j]); } System.out.println(); }举例:public class Test23TwoDimensionalArrayIterate { public static void main(String[] args) { //存储3个小组的学员的成绩,分开存储,使用二维数组。 int[][] scores = { {85,96,85,75}, {99,96,74,72,75}, {52,42,56,75} }; System.out.println("一共有" + scores.length +"组成绩."); for (int i = 0; i < scores.length; i++) { System.out.print("第" + (i+1) +"组有" + scores[i].length + "个学员,成绩如下:"); for (int j = 0; j < scores[i].length; j++) { System.out.print(scores[i][j]+"\t"); } System.out.println(); } } }内存解析二维数组本质上是元素类型是一维数组的一维数组。int[][] arr = { {1}, {2,2}, {3,3,3}, {4,4,4,4}, {5,5,5,5,5} };//1、声明二维数组,并确定行数和列数 int[][] arr = new int[4][5]; //2、确定元素的值 for (int i = 0; i < arr.length; i++) { for (int j = 0; j < arr.length; j++) { arr[i][j] = i + 1; } }//1、声明一个二维数组,并且确定行数 //因为每一行的列数不同,这里无法直接确定列数 int[][] arr = new int[5][]; //2、确定每一行的列数 for(int i=0; i<arr.length; i++){ /* arr[0] 的列数是1 arr[1] 的列数是2 arr[2] 的列数是3 arr[3] 的列数是4 arr[4] 的列数是5 */ arr[i] = new int[i+1]; } //3、确定元素的值 for(int i=0; i<arr.length; i++){ for(int j=0; j<arr[i].length; j++){ arr[i][j] = i+1; } }
2024年08月06日
11 阅读
0 评论
0 点赞
2024-08-05
Java枚举类
枚举类型本质上也是一种类,只不过是这个类的对象是有限的、固定的几个,不能让用户随意创建,它继承的是 java.lang.Enum类😀 枚举类的实现– 在 JDK5.0 之前需要程序员自定义枚举类型。私有化类的构造器,保证不能在类的外部创建其对象在类的内部创建枚举类的实例。声明为:public static final ,对外暴露这些常量对象对象如果有实例变量,应该声明为 private final(建议,不是必须),并在构造器中初始化 class Season { //季节的名称 private final String SEASONNAME;// //季节的描述 private final String SEASONDESC;// private Season(String seasonName, String seasonDesc) { this.SEASONNAME = seasonName; this.SEASONDESC = seasonDesc; } public static final Season SPRING = new Season("春天", "春暖花开 "); public static final Season SUMMER = new Season("夏天", "夏日炎炎 "); public static final Season AUTUMN = new Season("秋天", "秋高气爽 "); public static final Season WINTER = new Season("冬天", "白雪皑皑 "); @Override public String toString() { return "Season{" + "SEASONNAME='" + SEASONNAME + '\'' + ", SEASONDESC='" + SEASONDESC + '\'' + '}'; } } class SeasonTest { public static void main(String[] args) { System.out.println(Season.AUTUMN); } } – 在 JDK5.0 之后 Java 支持 enum 关键字来快速定义枚举类型。语法格式 【修饰符】 enum 枚举类名{ 常量对象列表 } 【修饰符】 enum 枚举类名{ 常量对象列表; 对象的实例变量列表; } 注意:枚举类的常量对象列表必须在枚举类的首行和普通 Java 类一样,枚举类可以实现一个或多个接口若每个枚举值在调用实现的接口方法呈现相同的行为方式,则只要统一实现该方法即可。若需要每个枚举值在调用实现的接口方法呈现出不同的行为方式,则可以让每个枚举值分别来实现该方法常用方法方法说明String toString()默认返回的是常量名(对象名),可以继续手动重写该方法static 枚举类型[] values()返回枚举类型的对象数组。该方法可以很方便地遍历所有的枚举值,是一个静态方法static 枚举类型 valueOf(String name)可以把一个字符串转为对应的枚举类对象。要求字符串必须是枚举类对象的“名字”。如不是,会有运行时异常:IllegalArgumentExceptionString name()得到当前枚举常量的名称。建议优先使用 toString()int ordinal()回当前枚举常量的次序号,默认从 0 开始public enum Month { /** 一月 */ JANUARY("01"), /** 二月 */ FEBRUARY("02"), /** 三月 */ MARCH("03"), /** 四月 */ APRIL("04"), /** 五月 */ MAY("05"), /** 六月 */ JUNE("06"), /** 七月 */ JULY("07"), /** 八月 */ AUGUST("08"), /** 九月 */ SEPTEMBER("09"), /** 十月 */ OCTOBER("10"), /** 十一月 */ NOVEMBER("11"), /** 十二月 */ DECEMBER("12"); /** 月份描述 */ private final String description; /** * 构造方法,初始化月份描述 * @param description 月份描述 */ Month(String description) { this.description = description; } /** * 根据月份描述获取月份枚举 * @param description 月份描述 * @return 对应的月份枚举,找不到返回null */ public static Month getMonth(String description) { for (Month month : Month.values()) { if (month.description.equals(description)) { return month; } } return null; } /** * 根据月份序号获取月份枚举 * @param month 月份序号(1-12) * @return 对应的月份枚举 */ public static Month getMonth(int month) { return Month.values()[month - 1]; } /** * 获取月份的天数 * @param leapYear 是否为闰年 * @return 月份的天数 */ public int length(boolean leapYear) { return switch (this) { case JANUARY, MARCH, MAY, JULY, AUGUST, OCTOBER, DECEMBER -> 31; case APRIL, JUNE, SEPTEMBER, NOVEMBER -> 30; default -> leapYear ? 29 : 28; }; } /** * 获取月份的默认天数(非闰年) * @return 月份的默认天数 */ public int days() { return length(false); } /** * 重写toString方法,输出月份描述和天数 * @return 月份描述和天数 */ @Override public String toString() { return "Month{" + "description='" + description + '\'' + ", days=" + days() + '}'; } }
2024年08月05日
26 阅读
0 评论
0 点赞
2024-08-04
内部类和代码块
什么是内部类?将一个类A定义在另一个类B里面,里面的那个类A就称为内部类,B则称为外部类。为什么要声明内部类呢?遵循高内聚低耦合的面向对象开发总原则。便于代码维护和扩展。 内部类形式根据内部类声明的位置(如同变量的分类)可以分为:(1)成员内部类:静态成员内部类非静态成员内部类(2)局部内部类有名字的局部内部类匿名的内部类成员内部类如果成员内部类中不使用外部类的非静态成员,那么通常将内部类声明为静态内部类,否则声明为非静态内部类。语法格式:【修饰符】 class 外部类{ 【其他修饰符】 【static】 class 内部类{ } }1、静态内部类有static修饰的成员内部类叫做静态内部类。它的特点:和其他类一样,它只是定义在外部类中的另一个完整的类结构可以继承自己的想要继承的父类,实现自己想要实现的父接口们,和外部类的父类和父接口无关可以在静态内部类中声明属性、方法、构造器等结构,包括静态成员可以使用abstract修饰,因此它也可以被其他类继承可以使用final修饰,表示不能被继承编译后有自己的独立的字节码文件,只不过在内部类名前面冠以外部类名和$符号。和外部类不同的是,它可以允许四种权限修饰符:public,protected,缺省,private外部类只允许public或缺省的只可以在静态内部类中使用外部类的静态成员在静态内部类中不能使用外部类的非静态成员哦如果在内部类中有变量与外部类的静态成员变量同名,可以使用“外部类名."进行区别在外部类的外面不需要通过外部类的对象就可以创建静态内部类的对象(通常应该避免这样使用)其实严格的讲(在James Gosling等人编著的《The Java Language Specification》)静态内部类不是内部类,而是类似于C++的嵌套类的概念,外部类仅仅是静态内部类的一种命名空间的限定名形式而已。所以接口中的内部类通常都不叫内部类,因为接口中的内部成员都是隐式是静态的(即public static)。例如:Map.Entry。2、非静态成员内部类没有static修饰的成员内部类叫做非静态内部类。非静态内部类的特点:和其他类一样,它只是定义在外部类中的另一个完整的类结构可以继承自己的想要继承的父类,实现自己想要实现的父接口们,和外部类的父类和父接口无关可以在非静态内部类中声明属性、方法、构造器等结构,但是不允许声明静态成员,但是可以继承父类的静态成员,而且可以声明静态常量。可以使用abstract修饰,因此它也可以被其他类继承可以使用final修饰,表示不能被继承编译后有自己的独立的字节码文件,只不过在内部类名前面冠以外部类名和$符号。和外部类不同的是,它可以允许四种权限修饰符:public,protected,缺省,private外部类只允许public或缺省的还可以在非静态内部类中使用外部类的所有成员,哪怕是私有的在外部类的静态成员中不可以使用非静态内部类哦就如同静态方法中不能访问本类的非静态成员变量和非静态方法一样在外部类的外面必须通过外部类的对象才能创建非静态内部类的对象(通常应该避免这样使用)如果要在外部类的外面使用非静态内部类的对象,通常在外部类中提供一个方法来返回这个非静态内部类的对象比较合适因此在非静态内部类的方法中有两个this对象,一个是外部类的this对象,一个是内部类的this对象package com.atguigu.inner.member; public class TestMemberInnerClass { public static void main(String[] args) { Outer.outMethod(); System.out.println("-----------------------"); Outer out = new Outer(); out.outFun(); System.out.println("####################################"); Outer.Inner.inMethod(); System.out.println("------------------------"); Outer.Inner inner = new Outer.Inner(); inner.inFun(); System.out.println("####################################"); Outer outer = new Outer(); // Outer.Nei nei = outer.new Nei(); Outer.Nei nei = out.getNei(); nei.inFun(); } } class Outer{ private static String a = "外部类的静态a"; private static String b = "外部类的静态b"; private String c = "外部类对象的非静态c"; private String d = "外部类对象的非静态d"; static class Inner{ private static String a ="静态内部类的静态a"; private String c = "静态内部类对象的非静态c"; public static void inMethod(){ System.out.println("Inner.inMethod"); System.out.println("Outer.a = " + Outer.a); System.out.println("Inner.a = " + a); System.out.println("b = " + b); // System.out.println("c = " + c);//不能访问外部类和自己的非静态成员 // System.out.println("d = " + d);//不能访问外部类的非静态成员 } public void inFun(){ System.out.println("Inner.inFun"); System.out.println("Outer.a = " + Outer.a); System.out.println("Inner.a = " + a); System.out.println("b = " + b); System.out.println("c = " + c); // System.out.println("d = " + d);//不能访问外部类的非静态成员 } } class Nei{ private String a = "非静态内部类对象的非静态a"; private String c = "非静态内部类对象的非静态c"; public void inFun(){ System.out.println("Nei.inFun"); System.out.println("Outer.a = " + Outer.a); System.out.println("a = " + a); System.out.println("b = " + b); System.out.println("Outer.c = " + Outer.this.c); System.out.println("c = " + c); System.out.println("d = " + d); } } public static void outMethod(){ System.out.println("Outer.outMethod"); System.out.println("a = " + a); System.out.println("Inner.a = " + Inner.a); System.out.println("b = " + b); // System.out.println("c = " + c); // System.out.println("d = " + d); Inner in = new Inner(); System.out.println("in.c = " + in.c); } public void outFun(){ System.out.println("Outer.outFun"); System.out.println("a = " + a); System.out.println("Inner.a = " + Inner.a); System.out.println("b = " + b); System.out.println("c = " + c); System.out.println("d = " + d); Inner in = new Inner(); System.out.println("in.c = " + in.c); } public Nei getNei(){ return new Nei(); } } 静态内部类非静态内部类类角色字节码文件外部类名$内部类名相同 修饰符public,缺省,abstract,final相同 父类或父接口可以相同 可以包含的成员所有成员==不允许有静态成员==成员角色修饰符public、protected、缺省、private,final,static没有static 依赖于外部类依赖相同 依赖于外部类的对象不依赖==依赖==使用在外部类中使用内部类没有限制在外部类的静态方法等中不能使用非静态内部类 在内部类中使用外部类静态内部类中不能使用外部类的非静态成员没有限制 在外部类的外面使用内部类的静态成员外部类名.静态内部类名.静态成员==没有== 在外部类的外面使用内部类的非静态成员见下面的框1见下面的框2重名 外部类名.重名的成员名外部类名.this.重名的成员外部类名.静态内部类名 变量 = 外部类名.静态内部类名(); 变量.非静态成员();外部类名 变量1 = new 外部类(); 外部类名.非静态内部类名 变量 = 变量1.new 非静态内部类名(); 变量.非静态成员();局部内部类1、局部内部类语法格式:【修饰符】 class 外部类{ 【修饰符】 返回值类型 方法名(【形参列表】){ 【final/abstract】 class 内部类{ } } }局部内部类的特点:和外部类一样,它只是定义在外部类的某个方法中的另一个完整的类结构可以继承自己的想要继承的父类,实现自己想要实现的父接口们,和外部类的父类和父接口无关可以在局部内部类中声明属性、方法、构造器等结构,但不包括静态成员,除非是从父类继承的或静态常量可以使用abstract修饰,因此它也可以被同一个方法的在它后面的其他内部类继承可以使用final修饰,表示不能被继承编译后有自己的独立的字节码文件,只不过在内部类名前面冠以外部类名、$符号、编号。这里有编号是因为同一个外部类中,不同的方法中存在相同名称的局部内部类和成员内部类不同的是,它前面不能有权限修饰符等局部内部类如同局部变量一样,有作用域局部内部类中是否能访问外部类的非静态的成员,取决于所在的方法局部内部类中还可以使用所在方法的局部常量,即用final声明的局部变量JDK1.8之后,如果某个局部变量在局部内部类中被使用了,自动加final为什么在局部内部类中使用外部类方法的局部变量要加final呢?考虑生命周期问题。示例代码:package com.atguigu.inner.local; public class TestLocalInner { public static void main(String[] args) { Runner runner = Outer.getRunner(); runner.run(); System.out.println("-------------------"); Outer.outMethod(); System.out.println("-------------------"); Outer out = new Outer(); out.outTest(); } } class Outer{ private static String a = "外部类的静态变量a"; private String b = "外部类对象的非静态变量b"; public static void outMethod(){ System.out.println("Outer.outMethod"); final String c = "局部变量c"; class Inner{ public void inMethod(){ System.out.println("Inner.inMethod"); System.out.println("out.a = " + a); // System.out.println("out.b = " + b);//错误的,因为outMethod是静态的 System.out.println("out.local.c = " + c); } } Inner in = new Inner(); in.inMethod(); } public void outTest(){ class Inner{ public void inMethod(){ System.out.println("out.a = " + a); System.out.println("out.b = " + b);//可以,因为outTest是非静态的 } } Inner in = new Inner(); in.inMethod(); } public static Runner getRunner(){ class LocalRunner implements Runner{ @Override public void run() { System.out.println("LocalRunner.run"); } } return new LocalRunner(); } } interface Runner{ void run(); }2、匿名内部类当我们在开发过程中,需要用到一个抽象类的子类的对象或一个接口的实现类的对象,而且只创建一个对象,而且逻辑代码也不复杂。那么我们原先怎么做的呢?(1)编写类,继承这个父类或实现这个接口(2)重写父类或父接口的方法(3)创建这个子类或实现类的对象这里,因为考虑到这个子类或实现类是一次性的,那么我们“费尽心机”的给它取名字,就显得多余。那么我们完全可以使用匿名内部类的方式来实现,避免给类命名的问题。new 父类(【实参列表】){ 重写方法... } //()中是否需要【实参列表】,看你想要让这个匿名内部类调用父类的哪个构造器,如果调用父类的无参构造,那么()中就不用写参数,如果调用父类的有参构造,那么()中需要传入实参new 父接口(){ 重写方法... } //()中没有参数,因为此时匿名内部类的父类是Object类,它只有一个无参构造匿名内部类是没有名字的类,因此在声明类的同时就创建好了唯一的对象。注意:匿名内部类是一种特殊的局部内部类,只不过没有名称而已。所有局部内部类的限制都适用于匿名内部类。例如:在匿名内部类中是否可以使用外部类的非静态成员变量,看所在方法是否静态在匿名内部类中如果需要访问当前方法的局部变量,该局部变量需要加final思考:这个对象能做什么呢?(1)使用匿名内部类的对象直接调用方法interface A{ void a(); } public class Test{ public static void main(String[] args){ new A(){ @Override public void a() { System.out.println("aaaa"); } }.a(); } }(2)通过父类或父接口的变量多态引用匿名内部类的对象interface A{ void a(); } public class Test{ public static void main(String[] args){ A obj = new A(){ @Override public void a() { System.out.println("aaaa"); } }; obj.a(); } }(3)匿名内部类的对象作为实参interface A{ void method(); } public class Test{ public static void test(A a){ a.method(); } public static void main(String[] args){ test(new A(){ @Override public void method() { System.out.println("aaaa"); } }); } }代码块普通成员代码块(非静态代码块)1、普通成员代码块的作用用于实例变量的初始化等操作。2、普通成员代码块的语法格式【修饰符】 class 类{ { 非静态代码块 } 【修饰符】 构造器名(){ // 实例初始化代码 } 【修饰符】 构造器名(参数列表){ // 实例初始化代码 } }3、普通成员代码块的执行特点所有非静态代码块中代码都是在new对象时自动执行。 执行一次构造器 就会执行一次代码块 并且一定是先于构造器的代码执行package com.atguigu.code; public class Student { String name; int age; { System.out.println("这是普通成员代码块"); name = "张三"; age = 30; int m = 10; } public Student() { System.out.println("--------Student()-------"); } public Student(String name, int age) { this.name = name; this.age = age; } { System.out.println("这是第二个代码块 "); } public void showInfo(){ System.out.println("name = "+name+",age = "+age); } } public class StudentTest { public static void main(String[] args) { Student s1 = new Student();//代码块先于构造器执行 s1.showInfo(); System.out.println("---------------------"); new Student("张三",30);//调用一次构造器执行一次代码块 } }静态成员代码块如果想要为静态变量初始化,可以直接在静态变量的声明后面直接赋值,也可以使用静态代码块。1、语法格式在代码块的前面加static,就是静态代码块。【修饰符】 class 类{ static{ 静态代码块 } }2、静态代码块的特点每一个类的静态代码块只会执行一次。静态代码块的执行优先于非静态代码块和构造器。package com.atguigu.keyword; public class Chinese { // private static String country = "中国"; private static String country; private String name; { System.out.println("非静态代码块,country = " + country); } static { country = "中国"; System.out.println("静态代码块"); } public Chinese(String name) { this.name = name; } }package com.atguigu.keyword; public class TestStaticBlock { public static void main(String[] args) { Chinese c1 = new Chinese("张三"); Chinese c2 = new Chinese("李四"); } } 3、静态代码块和非静态代码块静态代码块在类初始化时执行,只执行一次 非静态代码块在实例初始化时执行,每次new对象都会执行
2024年08月04日
16 阅读
0 评论
1 点赞
2024-08-03
密封类
Java 9 引入了密封类(Sealed Classes),旨在提供更严格的类型安全和更好的代码封装性。# Java 密封类 ## 概述 密封类是一种新的类类型,它允许限制继承它的类必须在同一个包中,或者必须是指定的某些类。这有助于限制类的使用,提高代码的安全性和可维护性。 ## 语法 要声明一个密封类,可以使用 `sealed` 关键字: sealed class Shape permits Circle, Square, Rectangle {} 在这个例子中,`Shape` 是一个密封类,它只允许 `Circle`、`Square` 和 `Rectangle` 三个类继承。 ## 特点 - **包限制**:密封类可以限制继承它的类必须在同一个包中。 - **继承限制**:密封类可以指定继承它的类必须是特定的几个类。 - **非密封子类**:密封类可以有非密封的子类,但这些子类不能被其他类继承。 ## 使用场景 - 当你想要限制类的继承结构时。 - 当你想要确保类的实现是封闭的,防止未来被错误的扩展。 ## 例子package shapes;sealed class Shape permits Circle, Square, Rectangle {}final class Circle extends Shape {void draw() { System.out.println("Drawing a circle"); }}class Square extends Shape {void draw() { System.out.println("Drawing a square"); }}class Rectangle extends Shape {void draw() { System.out.println("Drawing a rectangle"); }} 在这个例子中,`Shape` 类被密封,并且只有 `Circle`、`Square` 和 `Rectangle` 可以继承它。 ## 注意事项 - 密封类不能是 `public`,因为如果它是 `public`,则任何包都可以访问并继承它。 - 密封类和它的子类必须在同一个包中,或者子类必须被明确地用 `permits` 子句列出。 - 密封类可以提高代码的封装性,但也限制了类的使用灵活性。
2024年08月03日
5 阅读
0 评论
0 点赞
2024-08-03
面向对象核心
🍀 类的组成成员变量(属性):描述类的属性。成员方法(行为):描述类的行为。构造方法:为创建的对象分配内存并初始化。静态变量和静态方法:描述类的公共属性和行为(由类名打点调用)类是对象的模板,对象是类的实例。类通常包含属性(也称为成员变量或字段)和成员方法(成员函数),构造方法(函数),静态成员(静态变量和方法),共同描述了一个对象的状态和行为。(类可以是抽象的或也可以是具体的)对象是类的实例,通过new关键字来创建对象。静态成员于类本身,而不是属于类的某个实例的。也就是说,无论创建了多少个该类的对象,静态成员都只有一个副本,并且这个副本被所有对象共享,由类名打点调用。示例代码 // 这是一个表示人的类 public class Person { // 属性(也叫成员变量) private String name; private int age; // 构造方法,用来初始化对象 public Person(String name, int age) { this.name = name; this.age = age; } // 方法,表示人的行为 public void sayHello() { System.out.println("Hello, my name is " + name + " and I am " + age + " years old."); } // getter和setter方法,用来获取和设置属性 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 class Main { public static void main(String[] args) { // 使用构造方法创建对象 Person person1 = new Person("Alice", 30); person1.sayHello(); // 使用setter方法修改属性 person1.setAge(31); System.out.println("After one year, " + person1.getName() + " is now " + person1.getAge() + " years old."); } } 🍀 类的属性和方法属性属性是类的状态或特征,可以使用访问修饰符(如private、default、protected、public)来控制属性的访问权限 public class Car { // 属性 private String brand; private double price; // 静态属性 private static int count; // 构造方法 public Car(String brand, double price) { this.brand = brand; this.price = price; count++; } // getter和setter方法 public String getBrand() { return brand; } public void setBrand(String brand) { this.brand = brand; } public double getPrice() { return price; } public void setPrice(double price) { this.price = price; } // 静态方法 public static int getCount() { return count; } } 方法方法是类的行为或功能。方法的定义包括方法名、参数列表和返回类型 public class Calculator { // 实例方法 public int add(int a, int b) { return a + b; } // 静态方法 public static int subtract(int a, int b) { return a - b; } } 方法的调用 public class Main { public static void main(String[] args) { Calculator calc = new Calculator(); System.out.println("Addition: " + calc.add(5, 3)); System.out.println("Subtraction: " + Calculator.subtract(5, 3)); } } 构造方法构造方法是用于初始化对象的特殊方法,名称与类名相同,没有返回类型。{alert type="info"}访问修饰符 类名(参数列表) {// 构造方法体}"{/alert} public class Animal { String name; int age; // 构造方法 Animal(String name, int age) { this.name = name; this.age = age; } void display() { System.out.println("Name: " + name + ", Age: " + age); } } 静态变量和静态方法 静态变量和静态方法属于类,而不属于某个实例。静态变量用于存储类的公共属性,而静态方法用于实现类的公共行为 public class MathUtil { /** * 圆周率,一个静态常量 */ public static final double PI = 3.14159; /** * 计算平方数的方法 * * @param number 要计算平方的数字 * @return 平方结果 */ public static double square(double number) { return number * number; } } //对象实例化与方法调用 public class Main { public static void main(String[] args) { // 计算圆的面积 double area = MathUtil.PI * MathUtil.square(5); System.out.println("圆的面积:" + area); // 输出: 圆的面积:78.53975 } } 🍀 类的三大特性类有封装,继承,多态三大特性,了解他们之前需要先了解一下访问控制修饰符public: 访问权限最广,任何地方都可以访问。protected: 可以被同一包内的类和子类的访问。default: 没有显式指定修饰符时,默认为default。可以被同一包内的类访问。private: 访问权限最窄,只能被声明它的类访问。修饰符类同一包内子类其他包publicYesYesYesYesprotectedYesYesYesNodefaultYesYesNoNoprivateYesNoNoNo封装封装是通过将数据(属性)和行为(方法)封装在类中,保护数据不被外部直接访问。我们通过访问修饰符(如private、protected、public)来控制对属性和方法的访问。 public class Employee { private String name; private double salary; // getter和setter方法,用来安全的访问和修改私有属性 public String getName() { return name; } public void setName(String name) { this.name = name; } public double getSalary() { return salary; } public void setSalary(double salary) { this.salary = salary; } } public class Main { public static void main(String[] args) { Employee emp = new Employee(); emp.setName("John Doe"); emp.setSalary(50000); System.out.println("Employee Name: " + emp.getName()); System.out.println("Employee Salary: " + emp.getSalary()); } } 上面的this代表当前对象。在某个对象的方法内部,this 指的就是正在调用这个方法的对象本身。继承 继承允许一个类继承另一个类的属性和方法。被继承的类称为父类(或超类),继承的类称为子类(或派生类)。 // 父类 public class Animal { public void eat() { System.out.println("This animal eats food."); } } // 子类 public class Dog extends Animal { public void bark() { System.out.println("The dog barks."); } } 创建Dog对象可以调用继承自Animal类的方法 public class Main { public static void main(String[] args) { Dog dog = new Dog(); dog.eat(); // 输出: This animal eats food. dog.bark(); // 输出: The dog barks. } } 也可以用super关键字调用父类的构造方法、属性和方法多态多态允许同一个方法在不同对象中有不同的实现,可以通过方法重写或重载和接口实现来实现。方法重载 public class Calculator { // 重载方法,参数为两个整数 public int add(int a, int b) { return a + b; } // 重载方法,参数为两个双精度浮点数 public double add(double a, double b) { return a + b; } } 构造方法重载 public class Car { private String brand; private int year; // 构造方法重载,此处参数为品牌和年份 public Car(String brand, int year) { this.brand = brand; this.year = year; } // 构造方法重载,此处参数为品牌,赋值默认年份 public Car(String brand) { this(brand, 2022); // 调用另一个构造方法 } } 方法重写 //定义Animal接口 public interface Animal { // 定义了抽象方法,但并不提供具体的实现 void makeSound(); } public class Dog implements Animal { // 实现Animal接口的抽象类 @Override public void makeSound() { System.out.println("汪汪"); } } public abstract class Animal { // 动物发出的声音,子类必须实现 public abstract void makeSound(); // 动物睡觉 public void sleep() { System.out.println("Zzz"); } } public class Cat extends Animal { // 猫叫 @Override public void makeSound() { System.out.println("喵喵"); } } 抽象类,抽象方法和接口名称描述抽象类一种不能被实例化的类,作为其他类的基类,可以包含抽象方法和非抽象方法抽象方法没有方法体的方法,只声明方法名、返回值类型和参数列表。必须在抽象类中声明,并且由其子类来实现接口一种规范,定义了一组方法,但不提供具体的实现。接口中的所有方法都是抽象的共同点区别不能被实例化,体现了抽象概念抽象类可以包含非抽象方法,接口只能包含抽象方法能被实例化,体现了抽象概念抽象类可以有属性,接口只能有常量能被实例化,体现了抽象概念一个类可以实现多个接口,但只能继承一个抽象类 // 抽象类 abstract class Animal { // 抽象方法:动物发出的声音 public abstract void makeSound(); // 非抽象方法:动物睡觉 public void sleep() { System.out.println("Zzz"); } } // 接口 interface Pet { // 宠物的喂养方法 void feed(); } // 狗类,继承Animal抽象类,实现Pet接口 class Dog extends Animal implements Pet { @Override public void makeSound() { System.out.println("汪汪"); } @Override public void feed() { System.out.println("喂狗粮"); } }
2024年08月03日
28 阅读
0 评论
0 点赞
2024-08-02
Java基础语法
pre { background-color: #f0f0f0; } /* 为pre标签添加背景色 */ Java基础部分主要涵盖了数据类型,变量,运算符,分支语句,循环。📕 基本数据类型{message type="info" content="位宽:表示二进制位数,其中一位代表符号位"/}{mtitle title="整数型"/}类型位宽容量范围字节byte8($2^3$)$2^7$-128 到 1271short16($2^4$)$2^{15}$-32,768 到 32,7672int32($2^5$)$-2^{31}$到$2^{31}$-1-2,147,483,648 到 2,147,483,6474long64($2^6$)-$2^{64}$到$2^{64}$-1-9,223,372,036,854,775,808 到 9,223,372,036,854,775,8078{mtitle title="浮点型"/}类型位宽容量范围字节float32($2^6$)约$10^{47}$IEEE 754标准4double64($2^6$)约$10^{308}$IEEE 754标准8{mtitle title="字符型"/}类型位宽容量范围字节char16($2^4$)$2^{15}$-32,768 到 32,7672{mtitle title="逻辑型"/}类型位宽容量范围字节boolen不固定2true 或 false通常为1示例//byte类型 8位整数类型,范围从-128到127 byte a = 100; //short类型 16位整数类型,范围从-32,768到32,767 short b = 10000; //int类型 32位整数类型,范围从-2^31到2^31-1 int c = 100000; //long类型 64位整数类型,范围从-2^63到2^63-1 long d = 1000000000L; //float类型 32位单精度浮点数 float e = 3.14F; //double类型 64位双精度浮点数 double f = 3.141592653589793; //char类型 16位Unicode字符 char g = 'A'; //boolean类型 只有两个值:true和false boolean h = true; 📕 引用数据类型基本数据类型外的所有类型都是引用数据类型例如:类,接口,对象,数组等示例String str = "Hello, World!"; int[] arr = {1, 2, 3, 4, 5};注意 基本数据类型传值得到的是值的副本引用数据类型传值得到的是地址值📉 变量变量类型的声明与作用域局部变量 在方法体内声明,只能在该方法内使用实例变量 在类中,方法、构造函数之外声明,实例变量属于该类的对象,必须创建对象才能调用静态变量 静态变量属于类,该类不生产对象,通过类名就可以调用静态变量变量初始化声明变量时需要指定类型,初始化可以在声明时进行或稍后进行📠 运算符算数运算符加+, 减-, 乘*, 除/, 取模%int a = 10, b = 20; int sum = a + b; // 加法 int diff = b - a; // 减法 int product = a * b; // 乘法 int quotient = b / a; // 除法 int remainder = b % a; // 取模关系运算符等于==, 不等于!=, 大于>, 小于<, 大于等于>=, 小于等于<=boolean isEqual = (a == b); boolean isNotEqual = (a != b); boolean isGreater = (a > b); boolean isLesser = (a < b);逻辑运算符与&&, 或||, 非!boolean and = (a > 5 && b < 30); boolean or = (a > 15 || b < 30); boolean not = !(a > 5);赋值运算符基本赋值=, 加等+=, 减等-=, 乘等*=, 除等/=, 取模等%=int a = 10; a += 5; // 等同于 a = a + 5 a -= 3; // 等同于 a = a - 3 a *= 2; // 等同于 a = a * 2 a /= 2; // 等同于 a = a / 2 a %= 3; // 等同于 a = a % 3三元运算符条件表达式? 表达式1 : 表达式2int a = 10, b = 20; int max = (a > b) ? a : b; // 如果a大于b,max等于a,否则max等于b位运算符与&, 或|, 异或^, 取反~, 左移<<, 右移>>, 无符号右移>>>int x = 5; // 0101 int y = 3; // 0011 int and = x & y; // 0001 int or = x | y; // 0111 int xor = x ^ y; // 0110 int not = ~x; // 1010 int leftShift = x << 2; // 10100 int rightShift = x >> 2; // 0001 int unsignedRightShift = x >>> 2; // 0001{alert type="info"}在基本数据类型中,同一数据类型中不同数值范围的类型,小范围为大范围赋值存在自动类型提升,反之需要强制类型转换。{/alert}📝 控制语句条件语句if-else语句int age = 20; if (age >= 18) { System.out.println("成人"); } else if { System.out.println("未成年"); } else { System.out.println("数据错误"); }switch-case语句int day = 3; switch (day) { case 1: System.out.println("星期一"); break; case 2: System.out.println("星期二"); break; case 3: System.out.println("星期三"); break; default: System.out.println("其他"); }新特性public int getNumberOfWeekday(String day) { return switch (day) { case "Monday", "Tuesday", "Wednesday" -> 1; case "Thursday", "Friday" -> 2; case "Saturday" -> 3; case "Sunday" -> 4; default -> -1; }; }double feeRate = switch (transactionType) { case "DOMESTIC" -> { double rate = (transactionAmount < 1000) ? 0.01 : 0.005; yield rate; } case "INTERNATIONAL" -> { double rate = (transactionAmount < 1000) ? 0.02 : 0.015; yield rate; } case "TRANSFER" -> { double rate = (transactionAmount < 500) ? 0.005 : 0.002; yield rate; } default -> { yield 0.0; // 为无效的transactionType返回默认费率 } };循环语句for循环for (int i = 0; i < 5; i++) { System.out.println(i); }while循环int i = 0; while (i < 5) { System.out.println(i); i++; }do-while循环int i = 0; do { System.out.println(i); i++; } while (i < 5);跳转语句break语句用于终止循环或switch语句for (int i = 0; i < 10; i++) { if (i == 5) { break; // 当i等于5时跳出循环 } System.out.println(i); }continue语句用于跳过当前循环中的剩余语句,并开始下一次循环for (int i = 0; i < 10; i++) { if (i % 2 == 0) { continue; // 跳过当前循环的剩余部分 } System.out.println(i); // 只打印奇数 }return语句用于从方法返回值并终止方法的执行public int add(int a, int b) { return a + b; // 返回两个数的和 }💻 基础语法综合示例/** * 这是一个Java基础语法的示例类 * 包含变量声明、运算符使用、控制语句等 */ public class BasicSyntaxExample { public static void main(String[] args) { // 变量声明与初始化 int number = 10; double pi = 3.14159; boolean isJavaFun = true; char grade = 'A'; String message = "Hello, Java!"; // 运算符使用 int sum = number + 5; // 加法运算 double product = pi * 2; // 乘法运算 boolean isGreater = number > 5; // 关系运算 boolean isTrue = isJavaFun && (grade == 'A'); // 逻辑运算 // 输出结果 System.out.println("Number: " + number); System.out.println("Sum: " + sum); System.out.println("Product: " + product); System.out.println("Is number greater than 5: " + isGreater); System.out.println("Is Java fun and grade A: " + isTrue); System.out.println("Message: " + message); // 控制语句 if (isJavaFun) { System.out.println("Java is fun!"); } else { System.out.println("Java is not fun."); } switch (grade) { case 'A': System.out.println("Excellent!"); break; case 'B': System.out.println("Good!"); break; case 'C': System.out.println("Fair"); break; default: System.out.println("Invalid grade"); } // 循环语句 for (int i = 0; i < 5; i++) { System.out.println("For loop iteration: " + i); } int count = 0; while (count < 5) { System.out.println("While loop iteration: " + count); count++; } int n = 0; do { System.out.println("Do-while loop iteration: " + n); n++; } while (n < 5); // 跳转语句 for (int i = 0; i < 10; i++) { if (i == 5) { break; // 跳出循环 } System.out.println("Break example: " + i); } for (int i = 0; i < 10; i++) { if (i % 2 == 0) { continue; // 跳过当前循环的剩余部分 } System.out.println("Continue example: " + i); } } }
2024年08月02日
9 阅读
0 评论
0 点赞
1
2