编程语法细节

1.键盘录入中:

a.对于next和nextLine:

next语句遇到空格,回车(Enter),Tab键都会停止录入。

nextLine语句**只有遇到回车(Enter)**才会停止录入。

nextLine语句只能用于字符串录入


b.带有has的录入:返回值类型为布尔类型,例如boolean flag=it.hasNext();


c.static修饰键盘录入:

对于键盘录入时,会用到一个类Scanner->若一个方法中要用到类Scanner,则需要创建一次类Scanner对象;

若多个方法中要用到类Scanner,则需要每个方法中各创建一个类Scanner对象,

此时较麻烦,为了省内存,可把类Scanner写在类中方法外,用static修饰,归属于全类,这样就不需要多个方法中用到类Scanner时,每个方法中都创建一次类Scanner对象。

如下图:


2.全局变量和局部变量:

a.全局变量可以不赋初值,在对应方法内使用不报错;

b.局部变量如int a;,若之后没用到a,则int a;不报错,

若用到a,则int a;报错,此时需要为a赋初值,如int a=0;

c.局部变量没有默认值,全局变量有默认值:

a属于全局变量,即使没有赋值,也有默认值,可以进行输出打印等操作;

b属于局部变量,不存在默认值,如果没有赋值就进行输出打印等操作会报错,但如果只是定义局部变量不进行其他操作不报错。


3.异常:

a.源码:

NullPointerException类里有两个构造方法,第二个构造方法可以传字符串,比如写一些异常的内容。

RuntimeException类里也有两个构造方法,第二个构造方法可以传字符串,比如写一些异常的内容。

b.打印差异:

用printStackTrace方法会先把异常打印出来再执行第10行和第16行的语句。

会先把异常打印出来是因为ArrayIndexOutOfBoundsException不是自定义异常,所以会这样,

如果改用自定义异常 就能够顺序打出:

如:

java 复制代码
package a26practice;
​
public class Test {
    public static void main(String[] args) {
          Student s=new Student();
        try {
            s.setAge(-1);
        } catch (AgeException e) {
            System.out.println("---------------1");//先运行
            e.printStackTrace();
            /*运行结果为
            ---------------1
            a26practice.AgeException: 年龄是非负的
                at a26practice.Student.setAge(Test.java:20)
                at a26practice.Test.main(Test.java:7)
            */
        }
    }
}
​
​
class Student{
    private int age;
    public void setAge(int age) throws AgeException{
        if(age<0){
            throw new AgeException("年龄是非负的");
        }
        this.age=age;
    }
​
}
​
​
class AgeException extends RuntimeException{
    public AgeException(){}
    public AgeException(String message){
        super(message);
    }
}

第9行和第17行改用System .out .println(e);则会顺序打出

4.继承关系:

a.实例:

测试类:

javabean类:

注:调用父类某个成员变量时该成员变量不能用private修饰

super.num=1相当于调用父类的成员变量num,此时父类中num为1,因此第七行输出语句中super.num即再次调用父类num时此时num为1。->尽管num在父类中是全局变量,但不是默认值0。

第七行输出语句中super.num换成num也许, 第六行super.num=1相当于调用父类的成员变量num,此时父类中num为1,

第七行此时为num时遵循就近原则,先在本方法中找,发现没有,再在本类方法外找,发现也没有,最后到父类中找,发现找到了num,此时num为1,输出结果为1。

运行结果为:


b.构造方法:

1.子类即使没有写构造方法,也会有个无参数的默认构造方法(前提是没写别的构造方法),仍然会调用父类的构造方法(super()默认在子类无参构造第一行,调用父类无参构造)

2.构造方法不会被继承


c.子类继承下来的方法(即父类中非private,非static,非final的方法)只能操作子类继承了父类的

成员变量,也能操作父类中被隐藏的成员变量。


d.子类**重写或者新增的方法***不能直接操作*被子类隐藏的成员变量(被隐藏的成员变量是指父类中)。


e.上转型对象不能操作子类新增的成员变量和方法,只有上转型类对象可以强制转换为子类对象

(父类对象不能强制转换为子类对象)

上转型类对象:父类 命名=new 子类();

父类对象:父类 命名=new 父类();


f.子类重写父类方法后,子类对象的上转型对象访问的是子类重写后的方法。

(虽然先加载父类,但之后也会加载子类,如果有方法重写,父类的方法会被隐藏,则访问子类的方法)


g.不可以将父类对象的引用直接赋值给子类对象,

但可以将子类对象的引用直接赋值给父类对象;

java 复制代码
package a23practice;
​
public class Test {
    public static void main(String[] args) {
         Zi z=new Zi();
         Fu f=z;//------------>可以将子类对象的引用直接赋值给子父类对象
​
        /*Fu f=new Fu();
        Zi z=f;-------->不可以将父类对象的引用直接赋值给子类对象
​
        Fu f=new Zi();-->向上转型
         Zi z=f;----------->不可以将父类对象的引用直接赋值给子类对象
        */
    }
}
​
​
class Fu{
  Fu(){}
}
​
class Zi extends Fu{
    Zi(){
        super();
    }
}
复制代码

h.举例:

例一:
java 复制代码
package a24practice;
​
public class Test {
    public static void main(String[] args) {
          B b=new B();
          //b.money=200;-->private修饰了money,不能继承给B,B访问不了money
          //b.height=1.78f;-->height非私有,可以访问,但height是int型,不能用float型赋值
    }
}
​
​
class A{
    private int money=12;
    float height;
    int seeMoney(){
        return money;
    }
}
​
class B extends A{
    int height;
    int lookMoney(){
        int m=seeMoney();
        return m;
    }
}
​
例二:
java 复制代码
package a24practice;
​
public class Test {
    public static void main(String[] args) {
          A.f();
    }
}
​
​
class A{
    static int m;
    static void f(){
        m=20;
        System.out.println(m);
    }
}
​
class B extends A{
    /*void f(){ -------->有误,静态方法不能被重写
        m=222;
        System.out.println(m);
    }*/
}
​
例三:
java 复制代码
class A{
    public float getNum(){
        return 3.6f;
    }
}
​
class B extends A{
    /*public void getNum(){} -->构成了方法重写,因为方法名和参数列表相同,但仍旧不对,
                                因为返回值类型也需要相同->改成public float getNum(){ 方法体 }即可   */
    /*public void getNum(double d){} -->构成方法重载
    public double getNum(float d){ return 4.2; } -->构成方法重载
    public float getNum(){ return 5.6f; } -->构成方法重写*/
}
例四:
java 复制代码
package a24practice;
​
public class Test {
    public static void main(String[] args) {
        B b=new B(100);
        System.out.println(b.i);//运行结果为2
        /* b.i即调用了B的构造方法,此时i为2,输出2->B中有i这个变量,从A继承下来的(i在A中非私有),
           i在B类中是局部变量
         */
    }
}
​
​
class A{
    public int i=0;
    A(){
        i=1;
    }
}
​
class B extends A{
    B(int m){
        i=2;
    }
}
​
例五:
java 复制代码
package a24practice;
​
public class Test {
    public static void main(String[] args) {
        B b=new B();
​
        /* 无论子父类,只要出现了方法重载,会优先调用数据类型一致的方法*/
        
        System.out.println(b.f(3,5));//运行结果为15.0
        /* 3和5是整型,会调用B类里的f方法,由于返回值为double型,故结果为15.0 */
        
        System.out.println(b.f(3.0,5.0));//运行结果为8.0
        /* 3.0和5.0是浮点型,会调用A类里的f方法,由于返回值为double型,故结果为8.0 */
    }
}
​
​
class A{
    double f(double x,double y){
        return x+y;
    }
}
​
class B extends A{
    double f(int x,int y){
        return x*y;
    }
}
复制代码
例六:
java 复制代码
package a24practice;
​
public class Test {
    public static void main(String[] args) {
        A a=new A();
        System.out.println(a.getNum(10));//运行结果为30
        //创建A对象,再调用A里的getNum方法
​
        a=new B();
        System.out.println(a.getNum(10));//运行结果为200
        //子类对象的引用直接赋值给父类对象,B类重写了A类的getNum方法
        //此时隐藏了A类的getNum方法,用的是B类的getNum方法
    }
}
​
​
class A{
    int getNum(int a){
        return a+20;
    }
}
​
class B extends A{
    @Override
    int getNum(int b){ //-->方法重写
        return b*20;
    }
}
​
例七:
java 复制代码
package a24practice;
​
public class Test {
    public static void main(String[] args) {
        B b=new B();//创建了一个B类对象
        System.out.println(b.f(10.0,8.0));//运行结果为98.0
        /*调用B类的f方法,super.f相当于调用父类(A类)的f方法,得出m为18.0
          则m+x*y=18.0+10.0*8.0=98.0(方法返回值为double型)
         */
        System.out.println(b.g(3));//运行结果为12
        /*调用B类的g方法,A.g(3)即调用A类的g方法,参数为3-->m=3*3=9
          m+n=9+3=12
         */
​
        A a=new B();
        System.out.println(a.f(10.0,8.0));//运行结果为98.0
        /*此时为上转型对象,子类B类重写了A类的f方法,隐藏了A类的f方法,则
          用的是B类的f方法,
          -->调用B类的f方法,super.f相当于调用父类(A类)的f方法,得出m为18
             则m+x*y=18+10.0*8.0=98.0(方法返回值为double型)
         */
        System.out.println(a.g(3));//运行结果为9
        /* g为静态方法,a.g即类名.方法名调用了A类的g方法,结果为9
         */
    }
}
​
​
class A{
    double f(double x,double y){
        return x+y;
    }
    static int g(int n){
        return n*n;
    }
    /*static {
        方法体
    } -->这样的静态代码块运行一次后消失,而静态方法和静态变量不会运行后就消失
    */
}
​
class B extends A{
    double f(double x,double y){ //-->重写了A类的f方法
        double m=super.f(x,y);
        return m+x*y;
    }
    static int g(int n){
        int m=A.g(n);//g方法被static修饰,可类名.方法名去调用
        return m+n;
    }
}
复制代码
例八:
java 复制代码
package a24practice;
​
public class Test {
    public static void main(String[] args) {
        B b = new B();
        b.m = 20;//此时B类中的m为20
        System.out.println(b.getM());//运行结果为120
        //调用B类的getM()方法可得结果为120
​
        A a = b;
        a.m = -100;//此时A类中的m为-100
        System.out.println(a.getM());//运行结果为120
        /*A a = b;为上转型对象,B类中重写了父类(A类)的getM()方法,则此时用的是B类的getM()方法,
          而B类中的m仍旧为20(只是A类的m为-100),返回m+100=120-->a.getM()为120
         */
        System.out.println(b.seeM());//运行结果为-100
        /* b的类型为B类,调用的seeM()方法是A类继承下来的,此时A类中m为-100,seeM()方法返回-100,
           b.seeM()结果为-100
         */
    }
}
​
​
class A {
    int m;//非私有
​
    int getM() {
        return m;
    }
​
    int seeM() {
        return m;
    }
}
​
class B extends A {
    int m;//非私有
​
    int getM() {
        return m + 100;
    }
}
复制代码

5.多态:

a.强制类型转换-->模式变量

方法一:

方法二:使用模式变量


b.实现多态的三个必要条件:

one.继承-->有父子关系,才能看出父多了几个子

two.重写-->为了看出子类特有

three.父类引用指向子类对象即向上转型


c.instanceof关键字:

如C是B的子类,B是A的子类,c是C的对象,b是B的对象

则c instanceof B结果为true,因为c是B的子类

c instanceof A结果为true,因为c是A的子类

b instanceof C结果为false,因为b不是C的子类


6.异常:

a. 抛出RuntimeException异常会以红色文字打印在控制台。


b. finally关键字以及多重catch语句中,异常处理的顺序:

复制代码
         
java 复制代码
在Java中,多重`catch`语句用于捕获不同类型的异常。当使用多重`catch`语句时,异常处理的顺序是非常重要的,因为它决定了哪个`catch`块会处理抛出的异常。

以下是处理多重catch语句中异常顺序的规则:

  1. 子类异常在前,父类异常在后 :如果一个异常类是另一个异常类的子类,那么子类异常的catch块应该放在父类异常的catch块之前。这是因为子类异常会在父类异常之前被匹配。如果父类异常的catch块在子类异常之前,那么子类异常将永远不会被捕获,因为父类异常会先捕获所有异常。

例如,IOExceptionException的子类,因此如果你同时处理这两种异常,IOExceptioncatch块应该放在Exceptioncatch块之前。

java 复制代码
try {
    // 可能抛出异常的代码
} catch (IOException e) {
    // 处理IOException
} catch (Exception e) {
    // 处理其他Exception
}
  1. 具体异常在前,通用异常在后:更具体的异常应该首先被处理,然后是更通用的异常。这遵循了"最具体优先"的原则。

  2. finally块总是最后执行 :无论是否捕获到异常,finally块中的代码总是会执行(除非在trycatch块中使用了System.exit()或其他方式终止了程序)。finally块用于执行必须执行的清理操作,如关闭资源等。

注意:虽然Java允许使用多重catch语句,但过多的catch块可能会使代码难以阅读和维护。通常,最好只捕获你能够合理处理的异常,并允许其他异常冒泡到更高的调用栈级别。

最后,请确保你的代码适当地处理异常,避免忽略它们或仅仅打印堆栈跟踪。合理的异常处理是编写健壮和可靠代码的关键部分。


c.利用throws声明异常类型的意义:

复制代码
   在Java中,使用`throws`关键字声明异常类型的主要意义在于:
  1. 明确异常传播 :通过throws声明,方法可以向其调用者表明它可能会抛出哪些类型的异常。这允许调用者知道在调用该方法时可能需要处理哪些异常,从而确保程序的健壮性。

  2. 异常链的传递 :如果一个方法内部调用了其他可能抛出异常的方法,并且该方法不想或不能处理这些异常,那么它可以使用throws关键字将这些异常声明出来,以便其调用者可以处理它们。这有助于构建异常处理的层次结构,确保异常能够在适当的层次得到处理。

  3. 分离异常处理逻辑 :通过将异常声明为throws,方法可以将异常处理的逻辑与其核心功能分离。这有助于保持方法的简洁和专注于其主要职责,而将异常处理的责任交给调用者。

  4. 强制调用者处理异常 :如果方法使用throws声明了异常,那么任何调用该方法的代码都必须处理这些异常,要么通过try-catch块捕获并处理它们,要么通过在其方法签名中也使用throws关键字将它们继续向上层传播。这有助于确保不会忽略潜在的错误情况。

  5. 文档化可能的异常throws声明也是JavaDoc的一部分,因此它们也可以作为方法文档的一部分,告诉开发者该方法可能会抛出哪些类型的异常。这对于理解和使用API非常有帮助。

需要注意的是,使用throws声明异常并不意味着异常会被自动抛出。它只是表明方法可能会抛出这些异常,而实际是否抛出异常取决于方法内部的代码执行情况。同时,滥用throws可能会导致异常处理的责任被过度推给上层调用者,从而增加代码的复杂性和维护成本。因此,在使用throws声明异常时应该谨慎并考虑清晰的异常处理策略。


d.异常机制在实际项目中的应用:

复制代码
Java中的异常机制在实际项目中扮演着至关重要的角色。它提供了一种识别及响应错误的一致性机制,使得程序在遭遇异常情况时能够优雅地处理,避免程序崩溃或数据丢失等不可预料的问题。以下是异常机制在实际项目中的一些主要应用:
  1. 提高程序的稳定性和可靠性:通过添加异常处理代码,程序能够在遇到异常情况时做出适当的响应,而不是简单地崩溃。这有助于确保程序的稳定运行,并减少数据丢失或损坏的风险。

  2. 提高程序的可读性和可维护性:异常处理机制使得代码更加清晰和易于理解。通过将异常处理代码与正常业务代码分离,可以使代码结构更加整洁,并方便后续的维护和升级。

  3. 方便程序的调试和排错:异常处理机制可以帮助程序员快速定位程序中出现问题的位置和原因。通过捕获和处理异常,程序员可以更容易地找到并修复代码中的错误。

  4. 提高程序的安全性:通过对可能出现的异常进行处理,可以有效地防止程序被恶意攻击或非法操作所破坏。异常处理机制可以帮助识别并应对潜在的安全威胁,提高程序的安全性。

在实际项目中,异常处理通常需要根据具体需求和场景进行定制。程序员需要仔细分析可能出现的异常情况,并选择合适的异常处理策略。这包括使用try-catch语句块捕获和处理异常,以及使用throws关键字声明可能抛出的异常类型。

此外,异常链处理机制在实际项目中也非常有用。通过将多个异常的信息串联起来形成一个完整的异常栈,可以提高错误信息的可读性,降低代码维护成本,并有助于开发人员逐步追踪到问题的根源。

总之,Java的异常机制在实际项目中发挥着重要的作用,它提高了程序的稳定性、可靠性、可读性和可维护性,并方便了程序的调试和排错。合理地使用异常处理机制可以使程序更加健壮和安全。


e. catch模块在一个try...catch语句中执行一次或者0次


f. 异常关键字的作用:

catch模块* **用来捕获异常处理异常

throw模块 用来手动抛出异常

**try模块***放入可能发生异常的代码*

throws模块 用来声明异常

7.集合:

a.总结Collection:

b.List系列集合有关删除的操作:

java 复制代码
package com.itheima.a02myList;
​
import java.util.ArrayList;
import java.util.List;
​
public class A02_ListDemo2 {
    public static void main(String[] args) {
        //List系列集合中的两个删除的方法
        //1.直接删除元素
        //2.通过索引进行删除
​
        //1.创建集合并添加元素
        List<Integer> list=new ArrayList<>();
​
        list.add(1);
        list.add(2);
        list.add(3);
​
        //2.删除元素
        //这个删除的是1索引上的元素2,不是元素1
        /*原因:在调用方法的时候,如果方法出现了重载现象
            会优先调用实参与形参类型一致的那个方法
            */
        list.remove(1);//方法提示中remove(int index)
​
        System.out.println(list);//运行结果为[1, 3]
    }
}
复制代码

java 复制代码
package com.itheima.a02myList;
​
import java.util.ArrayList;
import java.util.List;
​
public class A02_ListDemo2 {
    public static void main(String[] args) {
        //List系列集合中的两个删除的方法
        //1.直接删除元素
        //2.通过索引进行删除
​
        //1.创建集合并添加元素
        List<Integer> list = new ArrayList<>();
​
        list.add(1);
        list.add(2);
        list.add(3);
​
        //2.删除元素
        //删除元素1的方法
        //法一:list.remove(1);
        /*法二:
        手动装箱,手动把基本数据类型的1变成Integer类型*/
        Integer i = Integer.valueOf(1);
        list.remove(i);//此时删除的是元素1
        //方法提示中是remove(Object o)
        /*原因:在调用方法的时候,如果方法出现了重载现象
        会优先调用实参与形参类型一致的那个方法*/
​
​
        System.out.println(list);//运行结果为[2, 3]
    }
}
​

8.静态:

a.静态代码块运行过程:

执行顺序: 父类 静态代码块和静态成员变量->子类 静态代码块和静态成员变量->父类 普通代码块和普通成员变量->父类 构造函数->子类 普通代码块和普通成员变量->子类构造函数

(总结:先父类再子类,先静态再普通。静态代码块无论父子类,都先运行)

举例:

java 复制代码
package a21practice;
​
public class B extends Object{
    static {
        System.out.println("Load B");
    }
    public B(){
        System.out.println("Create B");
    }
}
java 复制代码
package a21practice;
​
public class A extends B{
    static {
        System.out.println("Load A");
    }
    public A(){
        System.out.println("Create A");
    }
}
​
java 复制代码
package a21practice;
​
public class Testclass {
    public static void main(String[] args) {
        new A();
    }
}
​
复制代码

运行结果:

分析:

new了一个A对象,那么就要执行A类的构造方法来初始化对象,但是A类继承了B类 ,所以要先调用B类的无参 构造;在A和B类中都有静态代码块静态代码块是在加载类的时候进行执行,并且只执行一次,也就是说静态代码块在构造方法前进行执行,所以执行顺序是先执行B类的静态代码块,再执行A类的静态代码块,再执行B类的无参构造,最后执行子类的无参构造。(静态代码块执行一次常体现在循环中,比如第一次循环执行完静态代码块,第二次循环就不会执行该静态代码块)

9.接口:

a.接口的继承:

接口继承 的时候只能继承接口不能继承类,因为如果类可以存在非抽象的成员,如果接口继承了该类,那么接口必定从类中也继承了这些非抽象成员,这就和接口的定义相互矛盾,所以接口继承时只能继承接口。


b.接口的多继承:

接口可以多继承可以被多实现 ,因为接口中的方法都是抽象的,这些方法都被实现的类所实现,即使多个父接口中有同名的方法,在调用这些方法时调用的子类中被实现的方法,不存在歧义;同时,接口中只有静态的常量,但是由于静态变量是在编译期决定调用关系的,即使存在一定的冲突也会在编译时提示出错;而引用静态变量一般直接使用类名或接口名,从而避免产生歧义,因此也不存在多继承的第一个缺点。 对于一个接口继承多个父接口的情况也一样不存在这些缺点。所以接口可以多继承。


c.接口ListIterator,继承Iterator(迭代器):

hasNext方法和next方法结合能正向遍历集合,遍历后指针不会复位,此时可利用

hasPrevious方法和previous方法结合反向遍历集合。

迭代器创建后指针默认指向0索引。


d.接口类似类,可以创建对象,继承(接口只能继承接口)等。

10.遍历:

a.遍历结果差异:

java 复制代码
package com.itheima.a02myList;
​
import java.util.ArrayList;
import java.util.List;
import java.util.ListIterator;
​
public class A03_ListDemo3 {
    public static void main(String[] args) {
        //创建集合并添加元素
        List<String> list = new ArrayList<>();
        list.add("aaa");
        list.add("bbb");
        list.add("ccc");
​
        //列表迭代器遍历
         //获取一个列表迭代器对象,该列表迭代器里面的指针默认指向0索引
​
         //当中额外添加了一个方法:在遍历的过程中,可以添加元素->不能用add即集合添加元素的方法
         //只能用迭代器自身拥有的添加元素的方法->用迭代器调用
        ListIterator<String> it = list.listIterator();
        while (it.hasNext()){
            String s=it.next();
            if("bbb".equals(s)){
                //此时添加qqq
                it.add("qqq");//不能用集合添加即list.add("qqq");
            }
        }
        System.out.println(list);//循环外再打印,运行结果为[aaa, bbb, qqq, ccc]
    }
}
java 复制代码
package com.itheima.a02myList;
​
import java.util.ArrayList;
import java.util.List;
import java.util.ListIterator;
​
public class A03_ListDemo3 {
    public static void main(String[] args) {
        //创建集合并添加元素
        List<String> list = new ArrayList<>();
        list.add("aaa");
        list.add("bbb");
        list.add("ccc");
​
        //列表迭代器遍历
         //获取一个列表迭代器对象,该列表迭代器里面的指针默认指向0索引
​
         //当中额外添加了一个方法:在遍历的过程中,可以添加元素->不能用add即集合添加元素的方法
         //只能用迭代器自身拥有的添加元素的方法->用迭代器调用
        ListIterator<String> it = list.listIterator();
        while (it.hasNext()){
            String s=it.next();
            if("bbb".equals(s)){
                //此时添加qqq
                it.add("qqq");//不能用集合添加即list.add("qqq");
            }
            System.out.println(s);//循环中打印
        }
        /*运行结果为aaa
                  bbb
                  ccc*/
​
    }
}
​

b.集合中遍历类(如自己创建的Student类):

方式1:不包装
java 复制代码
package a25practice;
​
import java.util.ArrayList;
import java.util.Iterator;
​
public class Test {
    public static void main(String[] args) {
        ArrayList<Student> list=new ArrayList<>();
        Student s1=new Student(12,"zhangsan");
        Student s2=new Student(13,"lisi");
        list.add(s1);
        list.add(s2);
​
        Iterator<Student> it=list.iterator();
        while (it.hasNext()){
            Student s = it.next();//Student类
            System.out.println(s);
            /*运行结果为Student{age = 12, name = zhangsan}
                      Student{age = 13, name = lisi}*/
        }
    }
}
​
​
class Student {
    private int age;
    private String name;
​
​
    public Student(int age, String name) {
        this.age = age;
        this.name = name;
    }
​
​
    public String toString() {
        return "Student{age = " + age + ", name = " + name + "}";
    }
}

对于运行结果说明:

Student类里重写了顶级父类的toString方法

Student类里:public String toString() { return "Student{age = " + age + ", name = " + name + "}"; }

此时new一个Student对象会传重写后的toString里的内容:

如果不重写顶级父类的toString方法,new一个Student对象会传他的地址值:

方式2:包装
java 复制代码
package a25practice;
​
import java.util.ArrayList;
import java.util.Iterator;
​
public class Test {
    public static void main(String[] args) {
        ArrayList<Student> list=new ArrayList<>();
        Student s1=new Student(12,"zhangsan");
        Student s2=new Student(13,"lisi");
        list.add(s1);
        list.add(s2);
​
        Iterator<Student> it=list.iterator();
        while (it.hasNext()){
            String s = String.valueOf(it.next());//实际为Student类,手动包装为String类
            System.out.println(s);
            /*运行结果为Student{age = 12, name = zhangsan}
                      Student{age = 13, name = lisi}*/
        }
    }
}
​
​
class Student {
    private int age;
    private String name;
​
​
    public Student(int age, String name) {
        this.age = age;
        this.name = name;
    }
​
​
    public String toString() {
        return "Student{age = " + age + ", name = " + name + "}";
    }
}

源码:


c.增强for遍历:

未用增强for之前:

java 复制代码
//定义一个方法,用于对id进行唯一性判断
    //需要所有人的id即要一个集合,弄出里面的id,还需要输入的新id;需要返回,若有相同的,返回true;若没有相同的,返回false
    public static boolean contains(ArrayList<Person> list, String id) {
        for (int i = 0; i < list.size(); i++) { //
            //1.遍历得到集合中每一个学生
            Person p = list.get(i);
            //2.遍历得到集合中每一个学生id
            String pId = p.getId();
            //3.与输入的id进行比较
            if (id.equals(pId)) {
                //4.走if代表遇到了相同的,返回true
                return true;
            }
        }
        //5.循环完后还没结束方法,说明没有相同的
        return false;
    }

用增强for之后:

java 复制代码
//定义一个方法,用于对id进行唯一性判断
    //需要所有人的id即要一个集合,弄出里面的id,还需要输入的新id;需要返回,若有相同的,返回true;若没有相同的,返回false
    public static boolean contains(ArrayList<Person> list, String id) {
        for (Person p : list) { //此时直接得到学生对象
            //1.遍历得到集合中每一个学生id
            String pId = p.getId();
            //2.与输入的id进行比较
            if (id.equals(pId)) {
                //3.走if代表遇到了相同的,返回true
                return true;
            }
        }
        //4.循环完后还没结束方法,说明没有相同的
        return false;
    }

d.抽象类也可以实现接口。


11.方法重写与重载的区别:

对于重写 中,参数表相同是指参数类型必须一致 ,参数名可以不同

对于重载 中,参数表不同指参数类型不同 或者参数个数不同 或者参数顺序不同


12.类的规则:

a.不建议把类设计为public型,因为可能破坏类的封装性,

只能用友好修饰符public修饰符修饰权限。


b.一个java文件中最多只能有一个公共类(public修饰的类)

如:

Test .java代表一个java文件,此时Student类和Test类*只能***Test类是public类**

要想Student类能被public修饰,必须要在Student.java下申明


13.常见后缀:

复制代码
在Java中,源代码文件的后缀通常是.java。当编写Java程序时,会创建一个以.java为后缀的源代码文件,其中包含了Java源代码。例如,一个简单的Java源代码文件可以命名为`myclass.java`,其中`myclass`是类的名称,而`.java`则是文件的后缀。

此外,经过编译之后 的Java文件会生成以**.class**为后缀的文件,这是Java字节码文件。这些文件是Java虚拟机(JVM)可以理解和执行的。

在Java开发过程中,还会涉及到其他类型的文件,例如配置文件、资源文件等,它们通常会有不同的后缀,如.properties、.xml、.json等,但这些并不属于Java源代码文件或编译后的字节码文件。

请注意,文件的命名和后缀对于Java程序的正确运行和组织至关重要,因此需要遵循Java的命名规则和约定。


14.相加:

a.字符串字符可以相加-->直接加

b.数字 可以和字符相加-->此时字符用ASCII码参与计算

java 复制代码
package NiuKePractice;
​
public class Test {
    public static void main(String[] args) {
        System.out.println("a"+'a');//运行结果为aa
        System.out.println(1+'a');//运行结果为98
    }
}
复制代码

15.包装类如Integer(整数),Double(浮点数)转字符串:

如Integer:


16.接口补充:


17.字符是基本数据类型,字符串是引用数据类型-->两者不能强制转换


18.内部类可以被public,private,友好的,protected,static修饰


19.键盘录入补充:


20.继承关系的补充:

1.子类的有参构造函数和是否调用父类的有参数的构造函数无必然联系。

2.子类即使没有显示构造函数,也会有个无参数的默认构造函数(前提没写别的有参构造函数),仍然会调用父类的构造函数。


21.接口以及异常的补充:


22.接口WindowConstants-->用于设置关闭模式:


23.在Java中,抽象方法不能被private修饰。这是因为抽象方法的存在是为了让子类去实现的,而private修饰的方法只能被当前类访问,子类无法访问到被private修饰的父类方法,因此抽象方法不能使用private修饰。

如果需要在父类中定义一个能被子类访问的方法,可以考虑使用默认访问修饰符(即不使用任何修饰符)或protected修饰符,public也可以。这样定义的方法可以被同一个包中的类或子类访问,但不能被其他包的类访问。


24.异常补充:-->异常处理顺序

出现多个catch块时,应该先处理小异常,再处理大异常 ,也就是将处理父类异常 的catch块放在处理子类异常的catch块之Exception是所有异常的父类,如果处理Exception的catch语句块放在前面,会导致任何异常在这个catch块都能被匹配,后续catch块将变得没有意义。


25.以下程序执行后,错误的结果是()

java 复制代码
public class Test {
    private String name = "abc";
    public static void main(String[] args) {
        Test test = new Test();
        Test testB = new Test();
        String result = test.equals(testB) + ",";
        result += test.name.equals(testB.name) + ",";
        result += test.name == testB.name;
        System.out.println(result);
    }
}

第三个判断语句详解:

name成员变量并没有被static关键字修饰,那么每个Test类的对象被创建时,都会先执行private String name = "abc" ;(注意这里使用了直接赋值的方式,没有使用new关键字即没有创建新的对象)。在test对象初始化时,JVM会先检查字符串常量池中是否存在该字符串的对象,此时字符串常量池中并不存在"abc"字符串对象,JVM会在常量池中创建这一对象。当testB被创建时,同样会执行private String name = "abc";,那么此时JVM会在字符串常量池中找到test对象初始化时创建的"abc"字符串对象,那么JVM就会直接返回该字符串在常量池中的内存地址,而不会新建对象。也就是说test.name和testB.name指向的内存是同一个,那么test.name == testB.name返回true。

26.this和super关键字总结:


27.综合题:

对于B: 抽象类不一定有抽象方法,但如果一个类中有抽象方法,那么必定是抽象类.

对于C: protected成员在子类可见性可以修改: protected修饰的成员在子类中可见。可以在子类中重写父类的方法或变量的访问控制符,往大的修改。

对于E: final的的成员方法只能读取类的成员变量 这里的类的成员变量包括了两部分: 类变量和实例变量 另外,fianl修饰的方法不能被重写(覆盖)

对于F: String 是不可修改的,因为String 源码 有final修饰的属性value\[\]。


28.类的修饰:

a.static:

static修饰的为类成员,会随着类的加载而加载,比如静态代码块,静态成员,静态方法(这里只是加载,并没有调用)等等,可以想象一下,如果把一个Class文件中的外部类设为static,目的难道是让这个类随着应用的启动而加载吗?如果在这次使用过程中根本没有使用过这个类,那么是不是就会浪费内存。这样来说设计不合理,总而言之,设计不合理的地方,Java是不会让它存在的。 而为什么内部类可以使用static修饰呢,因为内部类算是类的成员了,如果没有使用静态来修饰,那么在创建内部类的时候就需要先有一个外部类的对象,如果我们一直在使用内部类,那么内存中就会一直存在外部类的引用,而我们有时候只需要使用内部类,不需要外部类,那么还是会浪费内存,甚至会造成内存溢出。使用static修饰内部类之后,内部类在创建对象时就不需要有外部类对象的引用了。 最终结论就是:static可以用来修饰内部类,但是不可以用来修饰外部类

b.修饰符:

内部类可以是静态static的,也可用public,友好的,default,protected和private修饰,

外部类的修饰符只能是public,abstract,final,友好的


29.声明多维数组的方式:(以整型为例)


30.导包:

在自定义类中,使用如上述图片的快捷方式时,要注意后面有导包的描述 ,一定要用对了,特别是自定义类有重复的情况下


31.\n是一个换行符:

注:若想要打印字符串\n,需要写 \ \n:

例一:

例二:

例三:

例四:


32.集合去重操作:

若集合底层是哈希表 结构,重写hashCode方法和equals方法以做到去重;

此外重写compareTo方法做到去重。


33.双列集合创建成数组举例:

java 复制代码
package a33MyMap;
​
import java.util.HashMap;
​
public class Test {
    public static void main(String[] args) {
        //1.创建集合数组对象
        HashMap<String,char>[] hm=new HashMap[5]; 
        //HashMap<String,char>[] hm= new java.util.HashMap<String, char>()[5];是错误的
    }
}

34.双列集合中键和值都不能是基本数据类型。

如果键和值要用到基本数据类型,则需要用包装类

如:HashMap <String ,Integer> hm=new HashMap<>();


35.双列集合在数组中添加元素的注意事项:

java 复制代码
package a34practice;
​
import java.util.HashMap;
import java.util.Map;
import java.util.function.BiConsumer;
​
public class Test {
    public static void main(String[] args) {
        //1.创建集合数组对象
        Map<String,String>[] m=new HashMap[1];
​
        //2.输入属性
         /* 1:m为数组名,代表数组地址值,m.put()是错误的,
              m[0].put才对
            2.在调用put方法前需要m[0]=new HashMap<>();
              不然的话直接添加就是一个null值
          */
        m[0]=new HashMap<>();
        m[0].put("张三","深圳");
​
        //3.遍历
         //此时m也只是数组名,无法调用forEach方法,m[0]才可以调用forEach方法,因为m[0]才是一个集合对象
        m[0].forEach(new BiConsumer<String, String>() {
            @Override
            public void accept(String key, String value) {
                System.out.println(key+"="+value);//运行结果为张三=深圳
            }
        });
    }
}
​

36.获取字符串每一个字符:(注:字符串的charAt方法可以获取指定索引上的字符)

字符串不能用增强for遍历


37.基本数据类型包装类:

Java基本数据类型(也称为原始数据类型)包括byteshortintlongfloatdoublecharboolean。为了能让这些基本数据类型像对象一样被操作(比如放入集合中、进行序列化等),Java为每种基本数据类型都提供了对应的包装类(Wrapper Class)。

这些包装类位于java.lang包中,它们分别是:

  1. Byte - 对应基本数据类型 byte

  2. Short - 对应基本数据类型 short

  3. Integer - 对应基本数据类型 int

  4. Long - 对应基本数据类型 long

  5. Float - 对应基本数据类型 float

  6. Double - 对应基本数据类型 double

  7. Character - 对应基本数据类型 char

  8. Boolean - 对应基本数据类型 boolean

使用包装类的目的

  • 作为集合的元素 :Java集合(如ArrayListHashMap等)只能存储对象引用,而不能直接存储基本数据类型。因此,当我们想要将基本数据类型存储在集合中时,就需要使用其对应的包装类。

  • 提供方法 :包装类提供了许多实用的方法,比如类型转换(parseIntparseDouble等)、基本类型与字符串的转换(toString)、比较大小(compareTo)等。

  • 泛型支持:Java泛型不支持基本数据类型,只能使用包装类。

自动装箱与拆箱

从Java 5(JDK 1.5)开始,引入了自动装箱(Autoboxing)和拆箱(Unboxing)的概念。这意味着基本数据类型与其对应的包装类之间的转换可以自动进行,无需手动编写代码进行转换。例如:

java 复制代码
Integer i = 10; // 自动装箱:int -> Integer
int j = i; // 自动拆箱:Integer -> int

注意事项

  • 虽然自动装箱和拆箱提供了便利,但它们可能会导致性能问题,因为涉及到对象的创建和销毁。

  • 某些情况下,自动装箱可能会导致意外的行为,比如Integer类型的缓存问题(在-128到127范围内的Integer对象是缓存的,超出这个范围则每次都会创建新对象)。

因此,在需要高性能或避免潜在问题时,建议直接使用基本数据类型而不是包装类,或者在必要时手动进行装箱和拆箱操作。


38.Arrays类常用方法(Arrays类用来操作数组):包括比较,排序,转换为字符串


39.有关数据类型转换:(注:方法最终的类型以方法定义时返回的类型为准,不是return语句后的类型)

例一:

java 复制代码
int x(int a,int b) { 
        return 1.2*(a+b); 
    }

上述代码会报错,因为返回的是浮点型(乘了个1.2),而方法定义时返回整型,浮点型比整型大,不能隐式转换从浮点型转为整型,因为大范围不能变到小范围的。

修改方案:可以在方法定义时返回double,如下

java 复制代码
double x(int a, int b) {
        return 1.2*(a+b);
    }

例二:

java 复制代码
float x( int a,int b ) {
        return (a-b);
    }

上述代码正确,a和b均为整型,所以返回的是整型,方法定义时返回浮点型,浮点型比整型大,能隐式转换从整型转为浮点型,因为小范围可以变到大范围的。

例三:

java 复制代码
int x( int a,int b) {
        return a-b;
    }

上述代码正确,返回的类型和方法定义时返回的类型一致,均为整型。

例四:

java 复制代码
int x( int a,int b ) { 
        return a*b; 
    }

上述代码正确,返回的类型和方法定义时返回的类型一致,均为整型。


40.双列集合用stream流的方法:

双列集合无法直接使用stream流,需要先用keySet方法(获取所有的键)或entrySet方法(获取到所有键值对对象的单列集合)将其转换为单列集合,才能使用stream流:


41.编写算法时一定要注意极端情况。


42.当图片无法显示时,一定要看图片路径和自己写的图片路径是否一样,不一样就无法显示。


43.符号->和.不能乱用:

"-> "的用处是使用一个指向以便访问 结构体或对象其内成员

". "的用处是使用一个指向以便访问结构体或对象


44.C语言中变量不用static,extern修饰的话默认为extern。


45.单链表中头结点只是没数据,但不是NULL。


46.顺序表和单链表一般都会设置一个变量记录长度,而且他们底层也自动有了长度,所以不需要设置成数组


47.C语言逻辑问题:

修改方案:


48.迭代和递归:

迭代和递归是计算机科学中两种常见的循环或重复执行的方式,它们在算法和数据结构的设计中扮演着重要角色。下面是关于迭代和递归的详细解释:

迭代(Iteration)

迭代是指通过循环结构(如for循环、while循环)来重复执行某段代码,直到满足特定的条件为止。在迭代过程中,每次循环都会使用上一次循环的结果作为下一次循环的初始值。迭代的核心是循环控制变量(如计数器)的更新,以及循环条件(何时停止循环)的判断。

迭代的特点包括:

  • 显式地使用循环结构。

  • 循环代码块中参与运算的变量同时是保存结果的变量。

  • 当前保存的结果作为下一次循环计算的初始值。

  • 使用计数器或条件判断来结束循环。

递归(Recursion)

递归是一种通过函数自身调用自身来解决问题的编程方法。在递归中,函数会不断地调用自身,直到满足某个基本条件(称为基本情况或基准情形)为止。递归的核心在于将大问题分解为小问题,直到问题变得足够小,可以直接解决为止,然后再将结果逐级返回。

递归的特点包括:

  • 显式或隐式地调用函数自身。

  • 存在一个或多个基本情况,用于停止递归调用。

  • 递归调用通常将问题分解为更小的问题,然后解决这些小问题。

  • 递归过程类似于树的深度优先遍历,从根节点开始,不断向下分解,直到叶子节点(基本情况),然后再逐级返回结果。

迭代与递归的区别

  1. 实现方式:迭代是通过循环结构实现的,而递归是通过函数自身调用自身实现的。

  2. 内存使用:大量的递归调用会建立函数的副本,可能耗费大量的时间和内存。而迭代则不需要反复调用函数和占用额外的内存。

  3. 代码复杂度:递归的代码通常更加简洁,因为它遵循了将问题分解为更小问题的自然思路。但是,递归的代码可能更难理解和调试,尤其是当递归深度很大时。迭代的代码则相对更加直观,但可能需要更长的代码来实现相同的功能。

  4. 适用场景:递归在处理具有递归性质的问题(如树形结构、图形遍历等)时非常有效。而迭代则更适合处理那些可以通过简单重复来解决的问题。

注意事项

  • 无限递归或无限迭代都可能导致程序崩溃或停止执行。因此,在使用递归或迭代时,必须确保存在明确的停止条件。

  • 在选择使用递归还是迭代时,需要考虑问题的性质、代码的可读性、性能要求以及内存使用等因素。

最后,需要指出的是,虽然递归在某些情况下可以提供更简洁的代码和更自然的解决方案,但在实际应用中,迭代由于其更高的执行效率和更低的内存使用,往往被更广泛地采用。


49.在遍历队列,修改队列指针等操作时,最好用取余运算(对队列最大长度进行取余)将存储空间在逻辑上变成了"环状",再进行操作,也是为了正确遍历队列中的元素,避免死循环。


50.Java中:字符,字符串,数字之间运算

字符与字符间运算是转化为ASCII码再运算,如'a'+'b'结果为195;

字符与字符串间运算结果是字符串,如'a'+"b"结果为字符串ab;

字符串与字符串间运算结果是字符串,如"a"+"b"结果为字符串ab;

字符串与数字运算结果是字符串与数字,如"a"+1结果为a1,如"123"+123结果为123123;

字符与数字运算结果是数字,其中字符转化为ASCII码参与计算,如'a'+1结果为98;

字符可以与数字比大小,如'a'>1结果为true,其中字符转化为对应的ASCII码参与计算,字符串不能与数字比大小,如"a">1语法错误;

字符可以与字符比大小,如'a'>'b'结果为false,其中字符转化为对应的ASCII码参与计算;

字符不可以与字符串比大小,如'a'>"b"语法错误;字符串不可以与字符串比大小,如"a">"b"语法错误;

字符串不可以与数字比大小,如1>'a'语法错误;

字符转化为整型是把字符转化为他对应的ASCII码,如'b'转化为整型为98,'0'转化为整型为48(不是0);


51.字符串类里的charAt方法:返回值为字符型char


52.顶级父类Object的toString方法:


53.对于一些如日期,年龄等数据最好做异常处理;


54.字符串类里的startsWith方法和endsWith方法:


55.基本数据类型如int的包装类Integer 里的parseInt方法:可以把字符串转换为对应的基本数据类型


56.C语言里的&符号:


57.二叉树的结点要么是根结点且二叉树的根结点只会有一个,要么不是根结点,不是根结点时该结点就只可能是左孩子结点或右孩子结点


58.变量范围:

例一:

cpp 复制代码
#include<stdio.h>
​
int main()
{
    int a=1;
    printf("a=%d \n",a); //运行结果为a=1 
    {
        a++;
        printf("a=%d \n",a); //运行结果为a=2
    }
    printf("a=%d \n",a);  //运行结果为a=2
    return 0;
}

例二:

cpp 复制代码
#include<stdio.h>
​
int main()
{
    int a=1;
    printf("a=%d \n",a); //运行结果为a=1 
    for(int i=0;i<1;i++)
    {
        a++;
        printf("a=%d \n",a); //运行结果为a=2
    }
    printf("a=%d \n",a);  //运行结果为a=2
    return 0;
}

59.变量访问细节:

例一:

java 复制代码
package a45practice;
​
public class Student {
    private int age;
​
    public Student(){}
    public Student(int age){
        this.age=age;
    }
​
    public void setAge(int age) {
        this.age = age;
    }
​
    public int getAge() {
        return age;
    }
}
​

类Student里的变量age被修饰符private修饰,因此在其他类中要想访问变量age,只能通过和变量age有关的方法进行访问:

java 复制代码
package a45practice;
​
public class Test {
    public static void main(String[] args) {
        Student s=new Student();
        s.setAge(10);
        System.out.println( s.getAge() ); //运行结果为10
    }
}
复制代码

例二:

java 复制代码
package a45practice;

public class Student {
    public int age;

    public Student(){}
    public Student(int age){
        this.age=age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public int getAge() {
        return age;
    }
}

类Student里的变量age被比访问范围大于修饰符private的修饰符修饰,因此在其他类中要想访问变量age,可以通过和变量age有关的方法进行访问,也可以通过s.age的形式访问:

java 复制代码
package a45practice;
​
public class Test {
    public static void main(String[] args) {
        Student s=new Student();
        s.setAge(10);
        System.out.println( s.getAge() ); //运行结果为10
​
        s.age=20;
        System.out.println( s.age ); //运行结果为20
    }
}
复制代码

60.数据能不写死,尽量不写死:

例如:

java 复制代码
package a45practice;
​
public class Test2 {
    public static void main(String[] args) {
        int[] arr={1,2,3};
​
        //求数组arr里的所有数据的和的平均值
        double sum = (arr[0]+arr[1]+arr[2]) / 3;
​
        System.out.println(sum); //运行结果为2.0
    }
}
复制代码

求平均值中,由于数组arrk长度易得知,所以直接写3即可,但如果数组长度无法快速得知,就需要写arr.length:

java 复制代码
package a45practice;

public class Test2 {
    public static void main(String[] args) {
        int[] arr={1,2,3};

        //求数组arr里的所有数据的和的平均值
        double sum = (arr[0]+arr[1]+arr[2]) / arr.length;

        System.out.println(sum); //运行结果为2.0
    }
}

由此可知,写arr.length更加灵活,可读性和效率也更高。


61.类的注意事项:


62.小细节1:


63.自幂数:


64.数字加密案例:


65.小细节2:


66.小细节3:


67.Java中的工具类的构造方法会被私有化,不让外界创建工具类的对象,而且工具类的大多数方法都被static修饰即静态化,因此类名.方法名即可调用工具类里的方法。


68.Java里的Comparator接口:


69.C语言中强制转换字符后的结果为字符对应的ASCII码:

例如:

cpp 复制代码
#include<stdio.h>
​
int main()
{
    int a=(int)'1';
    printf("%d",a);
    //运行结果为49,字符1强制转换为整型后结果为对应的ASCII码即49 
    return 0;
}

70.C语言中的三元运算符的细节:

cpp 复制代码
#include<stdio.h>
​
int main()
{
    int a=1,b=1;
    int c= a > b ? 3 : 4; 
    printf("%d",c); //运行结果为4,a、b相等时取后者 
    return 0;
}

相关推荐
j7~8 小时前
【C++】C++ 继承全解析:从基本语法到菱形继承的底层原理
开发语言·c++·继承·组合·虚继承·#暑假·七月创作之星博客挑战赛
SimonKing8 小时前
Agnes AI出桌面版了,可图可视频,免费用
java·后端·程序员
数行拙笔8 小时前
C++客户端---String类型
开发语言·c++·bootstrap
影寂ldy9 小时前
C# Task 进阶:WaitAll / WaitAny / WhenAll / WhenAny
开发语言·c#
左左右右左右摇晃9 小时前
手写Tomcat原理整理
java·开发语言·笔记·tomcat
孫治AllenSun9 小时前
【JVM】四大引用类型分析
java·开发语言·jvm
看-是灰机9 小时前
使用go语言实现对接
linux·开发语言·后端·docker·语言模型·golang·飞书
于慨9 小时前
安装java
java
草莓熊Lotso9 小时前
【Linux网络】深入理解Linux IO多路复用:从本质到select服务器实战
linux·运维·服务器·c语言·网络·数据库·c++