响应式网站欣赏,免费网站设计模板,搜索引擎营销的四种方式,罗湖区住房和建设局网站官网文章目录 面向对象 类与对象匿名对象创建对象的内存分析 栈(stack)堆(heap)方法区PC寄存器本地方法栈 内部类 成员内部类局部内部类匿名内部类静态内部类 包装类 拆箱和装箱操作字符串转换基本数据类型和包装类型的区别Integer类型的重点 抽象类 抽象方法不能被实例化常见问题抽…文章目录面向对象类与对象匿名对象创建对象的内存分析栈(stack)堆(heap)方法区PC寄存器本地方法栈内部类成员内部类局部内部类匿名内部类静态内部类包装类拆箱和装箱操作字符串转换基本数据类型和包装类型的区别Integer类型的重点抽象类抽象方法不能被实例化常见问题抽象类和普通类的区别接口面向接口编程思想全局常量和抽象方法的简写接口的实现 implements接口的继承 extends接口与抽象类的区别多态多态的体现多态的使用对象的类型转换InstanceofObject类Object的多态toString()equals()可变参数递归异常处理trycatch的处理流程finally异常体系结构throws关键字throw关键字RuntimeExcepion与Exception的区别自定义异常类try-with-resources构造方法构造器重载Overload重写Override重写与重载的区别Java两种核心机制JAVA跨平台原理标识符关键字thisstaticfinal权限修饰符封装private代码块包面向对象要理解面向对象思想我们先要知道什么是对象《Java编程思想》中提到“万物皆为对象”的概念。它将对象视为一种奇特的变量它除了可以存储数据之外还可以对它自身进行操作。它能够直接反映现实生活中的事物例如人、车、小鸟等将其表示为程序中的对象。每个对象都具有各自的状态特征也可以称为属性及行为特征方法java就是通过对象之间行为的交互来解决问题的。面向对象就是把构成问题的事物分解成一个个对象建立对象不是为了实现一个步骤而是为了描述某个事物在解决问题中的行为。类是面向对象中的一个很重要的概念因为类是很多个具有相同属性和行为特征的对象所抽象出来的对象是类的一个实例。类具有三个特性封装、继承和多态。三大特征封装核心思想就是“隐藏细节”、“数据安全”将对象不需要让外界访问的成员变量和方法私有化只提供符合开发者意愿的公有方法来访问这些数据和逻辑保证了数据的安全和程序的稳定。所有的内容对外部不可见。继承子类可以继承父类的属性和方法并对其进行拓展。将其他的功能继承下来继续发展 。多态同一种类型的对象执行同一个方法时可以表现出不同的行为特征。通过继承的上下转型、接口的回调以及方法的重写和重载可以实现多态。方法的重载本身就是一个多态性的体现。三大思想面向对象思想从概念上讲分为以下三种OOA、OOD、OOPOOA面向对象分析Object Oriented AnalysisOOD面向对象设计Object Oriented DesignOOP面向对象程序Object Oriented Programming 类与对象类表示一个共性的产物是一个综合的特征而对象是一个个性的产物是一个个体的特征。 类似生活中的图纸与实物的概念。类必须通过对象才可以使用对象的所有操作都在类中定义。类由属性和方法组成属性就相当于人的一个个的特征方法就相当于人的一个个的行为例如说话、吃饭、唱歌、睡觉一个类要想真正的进行操作则必须依靠对象对象的定义格式如下:类名称 对象名称 new 类名称() ;如果要想访问类中的属性或方法方法的定义则可以依靠以下的语法形式访问类中的属性 对象.属性 ;调用类中的方法 对象.方法(实际参数列表) ;类必须编写在.java文件中一个.java文件中可以存在N个类但是只能存在一个public修饰的类.java文件的文件名必须与public修饰的类名完全一直同一个包中不能有重名的类匿名对象没有对象名称的对象就是匿名对象。 即栈内存中没有名字而堆内存中有对象。匿名对象只能使用一次因为没有任何的对象引用所以将称为垃圾等待被GC回收。只使用一次的对象可以通过匿名对象的方式完成这一点在以后的开发中将经常使用到。public static void main(String[] args){ //Math2 mnew Math2(); //int numm.sum(100,200); //不通过创建对象名直接实例对象调用这就是匿名对象。因为没有对象名指向对象所以只能调用一次然后被GC回收。 int num new Math().sum(100,200); System.out.println(num); } class Math2{ int sum(int x,int y){ return xy; } }对象内存分析如下图所示创建对象的内存分析栈(stack)Java栈的区域很小 , 大概2m左右 , 特点是存取的速度特别快栈存储的特点是先进后出存储速度快的原因:栈内存, 通过 ‘栈指针’ 来创建空间与释放空间 !指针向下移动, 会创建新的内存, 向上移动, 会释放这些内存 !这种方式速度特别快 , 仅次于PC寄存器 !但是这种移动的方式, 必须要明确移动的大小与范围 ,明确大小与范围是为了方便指针的移动 , 这是一个对于数据存储的限制, 存储的数据大小是固定的 , 影响了程序 的灵活性 ~所以我们把更大部分的数据存储到了堆内存中堆存储的是:基本数据类型的数据以及引用数据类型的引用!例如:int a 10; Person p new Person();10存储在栈内存中 , 第二句代码创建的对象的引用§存在栈内存中堆(heap)存放的是类的对象 Java是一个纯面向对象语言, 限制了对象的创建方式 :所有类的对象都是通过new关键字创建new关键字, 是指告诉JVM , 需要明确的去创建一个新的对象 , 去开辟一块新的堆内存空间:堆内存与栈内存不同, 优点在于我们创建对象时 , 不必关注堆内存中需要开辟多少存储空间 , 也不需要关注内存占用时长 !堆内存中内存的释放是由GC(垃圾回收器)完成的垃圾回收器回收堆内存的规则 :当栈内存中不存在此对象的引用时,则视其为垃圾 , 等待垃圾回收器回收 !例如Person p0 new Person(); Person p1 p0; Person p2 new Person();堆在逻辑上分为三部分新生代Young Generation常称为YoungGen老年代Old Generation常称为OldGen、TenuringGen永久代Permanent Generation常称为PermGen新生区New/Young Generation新生代Young Generation常称为YoungGen位于堆空间。新生区又分为Eden区和Survior幸存区。Eden新创建的对象Survior 0、1经过垃圾回收但是垃圾回收次数小于15次的对象。养老区Old Generation老年代常称为OldGen位于堆空间Old垃圾回收次数超过15次依然存活的对象。永久区Permanent Generation永久代常称为PermGen位于非堆空间。永久区是一个常驻内存区域用于存放JDK自身所携带的ClassInterface的元数据也就是说它存储的是运行环境必须的类信息被装载进此区域的数据是不会被垃圾回收器回收掉的关闭JVM才会释放此区域所占用的空间。public static void main(String[] args){ String s1 123456; String s2 123456; System.out.println(s1s2)//结果true-----------第一次定义s1存放在堆中的永久区所以第二次属于调用 }方法区方法区Method Area又称永久代又称非堆区Non-Heap space方法区是被所有线程共享所有的字段和方法字节码以及一些特殊方法如构造函数接口代码也再此定义。简单说所有定义的方法的信息都保存在该区域此区属于共享区间。这些区域储存的是静态变量常量类信息构造方法/接口定义运行时常量池。但是实例变量存在堆内存中和方法区无关。以上只是逻辑上的定义。在HotSpot中方法区仅仅只是逻辑上的独立实际上还是包含在java堆中也就是说方法区在物理上属于java堆区中的一部分而永久区Permanent Generation就是方法区的实现。存放的是- 类信息- 静态的变量- 常量- 成员方法方法区中包含了一个特殊的区域 ( 常量池 )(存储的是使用static修饰的成员)方法区的实现的演变jdk1.7之前hotspot虚拟机对方法区的实现为永久代。jdk1.8及之后hotspot移除了永久代用元空间Metaspace。运行时常量池和字符串常量池的变化jdk1.7之前运行时常量池包含字符串常量池存放在方法区此时hotspot虚拟机对方法区的实现为永久代。jdk1.7字符串常量池被方法区拿到了堆中运行时常量池剩下的东西还在方法区也就是hotspot中的永久代。jdk1.8hotspot移除了永久代用元空间Metaspace取而代之。这时候字符串常量池还在堆中运行时常量池还在方法区只不过方法区的实现从永久代变成元空间Metaspace。代码使用内存情况如下图所示上图描述了程序运行时内存的情况当程序运行完毕栈内的会清空b2、b1这样堆内存中的Book对象就没有一个引用指向他即栈内存中没有指向他的则满足了GC的清理原则GC会自动清理掉堆内存中的Book对象。上图描述了两个对象b1、b2的在栈和堆中内存的使用情况当b2b1时b1指向的地址就覆盖了b2的指向地址这样原来b2对象在堆中的内存就没东西指向他的地址了这就满足了GC的自动清理原则。public static void main(String[] args){ String s1 锄禾日当午; String s2 汗滴禾下土; String s3 窗前明月光; text1 text1text2text3;//先计算text1text2产生地址为0x126的对象接着再计算0x126对象text3产生0x127对象 System.out.println(text1);//输出锄禾日当午汗滴禾下土窗前明月光 }上图描述了三个字符串拼接成一个新的字符串的内存使用情况可以看到栈中text1指向的地址被改变但是堆中产生两个没有指向的对象垃圾这是非常耗费内存的所以平常应该避免字符串拼接。PC寄存器PC寄存器保存的是当前正在执行的 JVM指令的 地址 在Java程序中, 每个线程启动时, 都会创建一个PC寄存器 本地方法栈保存本地(native)方法的地址内部类在Java中可以将一个类定义在另一个类里面或者一个方法里面这样的类称为内部类。广泛意义上的内部类一般来说包括这四种1、成员内部类2、局部内部类3、匿名内部类4、静态内部类成员内部类成员内部类是最普通的内部类它的定义为位于另一个类的内部形如下面的形式public class Demo{ public static void main(String[] args){ //外部使用成员内部类 Outer outter new Outer(100); Outer.Inner inner outter.new Inner(); inner.say(); //输出200 // 100 } } class Outer { private double x 0; public Outer(double x) { this.x x; } class Inner { private double x200; //内部类 public void say() { System.out.println(x); System.out.println(Outer.this.x); } } }特点 成员内部类可以无条件访问外部类的所有成员属性和成员方法包括private成员和静态成员。 不过要注意的是当成员内部类拥有和外部类同名的成员变量或者方法时会发生隐藏现象即默认情况下访问的是成员内部类的成员。如果要访问外部类的同名成员需要以下面的形式进行访问外部类.this.成员变量外部类.this.成员方法局部内部类局部内部类是定义在一个方法或者一个作用域里面的类它和成员内部类的区别在于局部内部类的访问仅限于方法内或者该作用域内。例如interface Person{ public void say(); } public class Demo{ public static void main(String[] args){ //局部内部类 class PersonImp implements Person{ Override public void say(){ System.out.prinln(新编写的局部内部类的say方法内容); } } PersonImp pnew PersonImp(); //这里像调用haha()方法但是需要一个Person类为此专门创建一个class文件类很浪费时间所以使用局部内部类 haha(p); } public static void haha(Person p){ } } //窗口关闭 public static void main(String[] args){ Frame fnew Frame(QQ登陆器); f.setVisible(true); f.setSize(300,200); class MyWindowListener implements WindowListener{ Override public void windowClosing(WindowEvent e){ System.out.println(哈哈哈); } } MyWindowListener lnew MyWindowListener(); //想要添加一个窗口关闭的事件可以使用局部类 f.addWindowListener(l); }注意:局部内部类就像是方法里面的一个局部变量一样是不能有public、protected、private以及static修饰符的。局部内部类也是只能访问final类型变量。匿名内部类匿名内部类由于没有名字所以它的创建方式有点儿奇怪。匿名内部类创建出来只能使用一次和匿名对象类似。创建格式如下new 父类构造器参数列表|实现接口 { //匿名内部类的类体部分 } interface Person{ public void say(); } public class Demo{ public static void main(String[] args){ //匿名内部类 Person pnew Person(){ public void say(){ System.out.println(锄禾日当午); } } haha(p); } public static void haha(Person p){ } }在这里我们看到使用匿名内部类我们必须要继承一个父类或者实现一个接口当然也仅能只继承一个父类或者实现一个接口。同时它也是没有class关键字这是因为匿名内部类是直接使用new来生成一个对象的引用。当然这个引用是隐式的。在使用匿名内部类的过程中我们需要注意如下几点1、使用匿名内部类时我们必须是继承一个类或者实现一个接口但是两者不可兼得同时也只能继承一个类或 者实现一个接口。2、匿名内部类中是不能定义构造函数的。3、匿名内部类中不能存在任何的静态成员变量和静态方法。4、匿名内部类为局部内部类所以局部内部类的所有限制同样对匿名内部类生效。5、匿名内部类不能是抽象的它必须要实现继承的类或者实现的接口的所有抽象方法。6、只能访问final型的局部变量。JDK1.8之后变量默认为final类型但是只要第二次赋值就不再是final类型的了。只能访问final类型的局部变量的原因因为局部类编译的时候是单独编译成一个文件所以在文件中有final变量的备份。静态内部类静态内部类也是定义在另一个类里面的类只不过在类的前面多了一个关键字static。静态内部类是不需要依赖于外部类对象的这点和类的静态成员属性有点类似并且它不能使用外部类的非static成员变量或者方法。格式public class Demo { public static void main(String[] args) { Book.Info info new Book.Info(); info.say(); } } class Book { static class Info { public void say(){ System.out.println(这是一本书); } } }包装类在Java中有一个设计的原则“一切皆对象”那么这样一来Java中的一些基本的数据类型就完全不符合于这种设计思想因为Java中的八种基本数据类型并不是引用数据类型所以Java中为了解决这样的问题引入了八种基本数据类型的包装类。以上的八种包装类可以将基本数据类型按照类的形式进行操作。但是以上的八种包装类也是分为两种大的类型的NumberInteger、Short、Long、Double、Float、Byte都是Number的子类表示是一个数字。ObjectCharacter、Boolean都是Object的直接子类。拆箱和装箱操作以下以Integer和Float为例进行操作将一个基本数据类型变为包装类那么这样的操作称为装箱操作。将一个包装类变为一个基本数据类型这样的操作称为拆箱操作因为所有的数值型的包装类都是Number的子类Number的类中定义了如下的操作方法以下的全部方法都是进行拆箱的操作。装箱操作在JDK1.4之前 如果要想装箱直接使用各个包装类的构造方法即可例如int temp 10 ; // 基本数据类型 Integer x new Integer(temp) ; // 将基本数据类型变为包装类 在JDK1.5Java新增了自动装箱和自动拆箱而且可以直接通过包装类进行四则运算和自增自建操作。例如 Float f 10.3f ; // 自动装箱 float x f ; // 自动拆箱 System.out.println(f * f) ; // 直接利用包装类完成 System.out.println(x * x) ; // 直接利用包装类完成字符串转换使用包装类还有一个很优秀的地方在于可以将一个字符串变为指定的基本数据类型此点一般在接收输入数据上使用较多。在Integer类中提供了以下的操作方法public static int parseInt(String s);//将String变为int型数据在Float类中提供了以下的操作方法public static float parseFloat(String s);//将String变为Float在Boolean 类中提供了以下操作方法public static boolean parseBoolean(String s);//将String变为boolean……基本数据类型和包装类型的区别1、包装类是对象拥有方法和字段对象的调用都是通过引用对象的地址基本类型不是2、包装类型是引用的传递基本类型是值的传递3、声明方式不同基本数据类型不需要new关键字而包装类型需要new在堆内存中进行new来分配内存空间4、存储位置不同基本数据类型直接将值保存在值栈中而包装类型是把对象放在堆中然后通过对象的引用来调用他们5、初始值不同eg int的初始值为 0 、 boolean的初始值为false 而包装类型的初始值为null6、使用方式不同基本数据类型直接赋值使用就好 而包装类型是在集合如 coolection Map时会使用Integer类型的重点Integer a1000,b1000; System.out.println(ab); /** 上述代码返回false因为 IntegerCache.low -128 IntegerCache.high 127 所以上面Integer a 1000,b 1000;其实都是new Integer(1000);所以分配的内存地址肯定不一样,所以比较就成false了 */抽象类抽象类必须使用abstract class声明一个抽象类中可以没有抽象方法。抽象方法必须写在抽象类或者接口中。格式abstract class 类名{ // 抽象类 }抽象方法只声明而未实现的方法称为抽象方法未实现指的是没有“{}”方法体抽象方法必须使用abstract关键字声明。格式// 抽象类 abstract class 类名{ public abstract void 方法名() ; // 抽象方法只声明而未实现 }不能被实例化在抽象类的使用中有几个原则抽象类本身是不能直接进行实例化操作的即不能直接使用关键字new完成。 不能被我们创建但是jvm虚拟器可以创建。一个抽象类必须被子类所继承被继承的子类如果不是抽象类则必须覆写(重写)抽象类中的全部抽象方法。常见问题1、 抽象类能否使用final声明不能因为final属修饰的类是不能有子类的 而抽象类必须有子类才有意义所以不能。2、 抽象类能否有构造方法能有构造方法而且子类对象实例化的时候的流程与普通类的继承是一样的都是要先调用父类中的构造方法默认是无参的之后再调用子类自己的构造方法。抽象类和普通类的区别1、抽象类必须用public或protected修饰(如果为private修饰那么子类则无法继承也就无法实现其抽象方法。 默认缺省为 public 2、抽象类不可以使用new关键字创建对象 但是在子类创建对象时 抽象父类也会被JVM实例化 3、如果一个子类继承抽象类那么必须实现其所有的抽象方法。如果有未实现的抽象方法那么子类也必须定义为 abstract类 接口如果一个类中的全部方法都是抽象方法全部属性都是全局常量那么此时就可以将这个类定义成一个接口。定义格式interface 接口名称{ 全局常量 ; 抽象方法 ; }面向接口编程思想这种思想是接口是定义规范约束与实现名实分离的原则的分离。优点1、 降低程序的耦合性2、 易于程序的扩展3、 有利于程序的维护全局常量和抽象方法的简写因为接口本身都是由全局常量和抽象方法组成 所以接口中的成员定义可以简写1、全局常量编写时 可以省略public static final 关键字例如public static final String INFO 内容 ; //简写后 String INFO 内容 ;2、抽象方法编写时 可以省略 public abstract 关键字 例如public abstract void print() ; //简写后 void print() ;接口的实现 implements接口可以多实现格式class 子类 implements 父接口1,父接口2...{ } //以上的代码称为接口的实现。那么如果一个类即要实现接口又要继承抽象类的话则按照以下的格式编写即可 class 子类 extends 父类 implements 父接口1,父接口2...{ }注意如果一个接口要想使用必须依靠子类。 子类如果不是抽象类的话要实现接口中的所有抽象方法。接口的继承 extends继承是java面向对象编程技术的一块基石因为它允许创建分等级层次的类。继承就是子类继承父类的特征和行为使得子类对象实例具有父类的实力域和方法或子类从父类继承方法使得子类具有父类相同的行为。继承的限制Java中只有单继承多重继承没有多继承即一个子类只能有一个父类。多重继承通俗来讲就是爷爷、爸爸、孙子。接口因为都是抽象部分 不存在具体的实现 所以允许多继承,例如interface C extends A,B{ }student类实例化时先实例化person默认调用的person的无参构造方法public class Demo{ public static void main(String[] args){ Student student new Student(); student.say(); } } class Person{ private String name; private int age; public Person(){ supper();//平时supper()可以省略作用时默认调用父类的无参构造方法 } public Person(String name,int age){ this.namename; this.ageage; } public void say(){ System.out.println(姓名name年龄age); } } class Student extends Person{ Student(){ supper(张三,1); } } //结果为 //姓名张三年龄1supper通过supper可以访问父类的构造方法、属性、方法。通过supper调用父类构造方法的代码必须写在第一行。supper和this调用构造函数时都需要放在第一行但是两者不会同时使用因为不可能调用自身构造函数的同时还调用父类的构造方法接口与抽象类的区别1、抽象类要被子类继承接口要被类实现。2、接口只能声明抽象方法抽象类中可以声明抽象方法也可以写非抽象方法。3、接口里定义的变量只能是公共的静态的常量抽象类中的变量是普通变量。4、抽象类使用继承来使用 无法多继承。 接口使用实现来使用 可以多实现5、抽象类中可以包含static方法 但是接口中不允许静态方法不能被子类重写因此接口中不能声明静态方法6、接口不能有构造方法但是抽象类可以有7、1.8后接口允许出现有方法体的方法多态多态就是对象的多种表现形式多种体现形态多态的体现对象的多态性从概念上非常好理解在类中有子类和父类之分子类就是父类的一种形态 对象多态性就从此而来。ps: 方法的重载 和 重写 也是多态的一种 不过是方法的多态相同方法名的多种形态。重载一个类中方法的多态性体现 。重写子父类中方法的多态性体现。多态的使用对象的类型转换类似于基本数据类型的转换向上转型将子类实例变为父类实例 |- 格式父类 父类对象 子类实例 向下转型将父类实例变为子类实例 |- 格式子类 子类对象 子类父类实例 public class Demo{ public static void main(String[] args){ Student student1new Student(); Nurse nurse1new Nurse(); //向上转型父类引用指向子类对象 Person person1student1; person1.say(); //输出我是学生 Person person2nurse1; person2.say(); //输出我是护士 //向下转型 Student student2(Student)person1; student2.say(); //输出我是学生 //向下转型需要注意的是不能把原来是护士的张三转成学生 例如 Student student3(Student)person2; student3.say(); //此处会报错 //向上转型比较高级的用法 Student student4new Student(); say(student4); //输出我是学生 } public static void say(Person person){ person.say(); } } abstract class Person{ public abstract void say(); } class Student extends Person{ Override public void say(){ System.out.println(我是学生); } } class Nurse extends Person{ Override public void say(){ System.out.println(我是护士) } }注意向上转型的对象是通过父类调用子类覆盖或继承父类的方法不是父类的方法。而且此时父类对象不能调用子类特有的方法。Instanceof作用判断某个对象是否是指定类的实例则可以使用instanceof关键字格式实例化对象 instanceof 类 //此操作返回boolean类型的数据Object类Object类是所有类的父类基类如果一个类没有明确的继承某一个具体的类则将默认继承Object类。//例如我们定义一个类 public class Person{ } //其实它被使用时 是这样的 public class Person extends Object{ }Object的多态使用Object可以接收任意的引用数据类型public static void main(String[] args){ String text123; say(text); int a10; say(a); } public static void say(Object o){ System.out.println(o) }toString()建议重写Object中的toString方法。 此方法的作用返回对象的字符串表示形式 Object的toString方法 返回对象的内存地址 System.out.println(对象名)一般输出时调用的时对象的toString方法 equals()建议重写Object中的equals(Object obj)方法此方法的作用指示某个其他对象是否“等于”此对象。***Object的 equals方法***实现了对象上最具区别的可能等价关系; 也就是说对于任何非空引用值x和y 当且仅当 x和y引用同一对象 x y具有值true 时此方法返回true 。equals方法重写时的五个特性自反性 对于任何非空的参考值x x.equals(x)应该返回true 。对称性 对于任何非空引用值x和y x.equals(y)应该返回true当且仅当y.equals(x)回报true 。传递性 对于任何非空引用值x y和z 如果x.equals(y)回报true个y.equals(z)回报true 然后 x.equals(z)应该返回true 。一致性 对于任何非空引用值x和y 多次调用x.equals(y)始终返回true或始终返回false 前提是未修改对象上的equals比较中使用的信息。非空性 对于任何非空的参考值x x.equals(null)应该返回false 。class Person{ private String name; private int age; public boolean equals(Object o){ //判断内存地址是否相同 if(thiso){ return true; } //非空性 if(onull){ return false; } //判断是否是同一个类 if(o instanceof Person){ //向下转型 Person p2(Person)o; //此处调用的是String里的equals()方法和Object不同 if(this.name.equals(p2.name)this.agep2.age){ return true; } } return false; } }equals和的区别前者是比较两个数是否等价后者是比较地址可变参数一个方法中定义完了参数则在调用的时候必须传入与其一一对应的参数但是在JDK 1.5之后提供了新的功能可以根据需要自动传入任意个数的参数。语法返回值类型 方法名称(数据类型…参数名称){ //参数在方法内部 以数组的形式来接收 } public class Demo{ public static void main(String[] args){ System.out.println(sum(1)); //输出1 System.out.println(sum(1,2)); //输出3 System.out.println(sum(1,2,3)); //输出6 System.out.println(sum(1,2,3,4)); //输出10 } public static int sum(int... nums){ int n0; for(int i0;inums.length;i){ nnum[i]; } return n; } }注意 可变参数只能出现在参数列表的最后。递归递归在数学与计算机科学中是指在方法的定义中使用方法自身。也就是说递归算法是一种直接或者间接调用自身方法的算法。递归流程图如下//5的阶乘 public class Demo{ public static void main(String[] args){ int nfact(5); System.out.println(n); //结果120 } public static int fact(int n){ if(n1){ return 1; }else{ n*fact(n-1); } } }注意能用循环完成的工作尽量不要使用递归因为太消耗内存。异常处理异常是在程序中导致程序中断运行的一种指令流。例如现在有如下的操作代码public class ExceptionDemo01{ public static void main(String argsp[]){ int i 10 ; int j 0 ; System.out.println( 计算开始 ) ; int temp i / j ; // 进行除法运算 System.out.println(temp temp) ; System.out.println( 计算结束 ) ; } }; //运行结果 // 计算开始 Exception in thread main java.lang.ArithmeticException: / by zero at ExceptionDemo01.main(ExceptionDemo01.java:6)以上的代码在“int temp i / j ;”位置处产生了异常一旦产生异常之后异常之后的语句将不再执行了所以现在的程序并没有正确的执行完毕之后就退出了。那么为了保证程序出现异常之后仍然可以正确的执行完毕所以要采用异常的处理机制。如果要想对异常进行处理则必须采用标准的处理格式处理格式语法如下try{ // 有可能发生异常的代码段 }catch(异常类型1 对象名1){ // 异常的处理操作 }catch(异常类型2 对象名2){ // 异常的处理操作 } ... finally{ // 异常的统一出口 } public class ExceptionDemo01{ public static void main(String argsp[]){ int i 10 ; int j 0 ; System.out.println( 计算开始 ) ; try{ int temp i / j ; // 进行除法运算 System.out.println(temp temp) ; System.out.println( 计算结束 ) ; }catch(ArithmeticException e){ System.out.println(除数不能为零) ; } } };trycatch的处理流程1、 一旦产生异常则系统会自动产生一个异常类的实例化对象。2、 那么此时如果异常发生在try语句则会自动找到匹配的catch语句执行如果没有在try语句中则会将异常抛出.3、 所有的catch根据方法的参数匹配异常类的实例化对象如果匹配成功则表示由此catch进行处理。注意使用try…catch捕获异常不是简单的提示就行了那样意义很小我们应该想办法解决异常。finally在进行异常的处理之后在异常的处理格式中还有一个finally语句那么此语句将作为异常的统一出口不管是否产生了异常最终都要执行此段代码。注意finally在一些情况是不会被执行的比如电脑被关机了方法强制中断。唯一会使finally不执行的代码public class Demo{ public static void main(String[] args){ haha(); } public static void haha(){ try{ int a 10; int b 0; System.out.println(a/b); }catch(Exception e){ System.out.println(出现了异常); //退出JVM System.exit(0); }finally{ System.out.println(锄禾日当午汗滴禾下土); } } } //结果出现了异常finally两种执行情况//案例一 public class Demo1{ public static void main(String[] args){ Person phaha(); System.out.println(p.age); //输出28 } public static Person haha(){ Person pnew Person(); try{ p.age18; return p; }catch(Exception e){ return null; }finally{ p.age28; } } static class Person{ int age; } } //finally会再return 准备数据返回的阶段执行所以无论是否returnfinally都是执行。 //案例二 public class Demo2{ public static void main(String[] args){ int a haha(); System.out.println(a); //输出10 } public static int haha(){ int a 10; try{ return a; }catch(Exception e){ return null; }finally{ a 20; } } } //结果和案例一不一样是因为两者返回的数据类型不一样。 //案例一返回的是引用数据类型在return的准备数据返回的阶段备份的是堆内存地址所以堆内存里的Person对象的age改变return备份的值都会改变。 //案例二返回的是基本数据类型在return的准备数据返回阶段备份的是值即10所以无论栈内存中的a如何改变都不会影响return备份的10。案例一内存使用情况如下图所示案例二内存使用情况如下图所示异常体系结构异常指的是Exception Exception类 在Java中存在一个父类Throwable可能的抛出Throwable存在两个子类Error表示的是错误是JVM发出的错误操作,只能尽量避免无法用代码处理。Exception一般表示所有程序中的错误所以一般在程序中将进行try…catch的处理。受检异常代码会飘红不受检异常不会。多异常捕获的注意点1、 捕获更粗的异常不能放在捕获更细的异常之前。2、 如果为了方便则可以将所有的异常都使用Exception进行捕获。特殊的多异常捕获写法catch(异常类型1 |异常类型2 对象名){ //表示此块用于处理异常类型1 和 异常类型2 的异常信息 } //还有可以直接使用Exception类捕获异常这样所有的异常都能捕获缺点是针对性差throws关键字在程序中异常的基本处理已经掌握了但是随异常一起的还有一个称为throws关键字此关键字主要在方法的声明上使用表示方法中不处理异常而交给调用处处理。格式返回值 方法名称()throws Exception{ }如果是传参导致的异常应该通过throws将异常抛出去public class Demo{ public static void main(String[] args){ shutDown(0);//此处也是受检异常飘红因为shutDown()方法把异常抛给了调用者所以需要捕获或者抛出异常。 } public static void shutDown(String text) throws IOException{ Runtime.getRuntime().exec(text);//此处为受检异常飘红需要捕获或者抛出异常 } //通俗点来讲抛出异常就是告诉调用者我这个方法有异常你需要处理。 }throw关键字throw关键字表示在程序中人为的抛出一个异常因为从异常处理机制来看所有的异常一旦产生之后实际上抛出的就是一个异常类的实例化对象那么此对象也可以由throw直接抛出。代码throw new Exception(抛着玩的。) ;示例public class Demo{ public static void main(String[] args){ Person person new Person(); person.setAge(-1); } } class Person{ private int age; public void setAge(int age){ if(age0 || age180){ RuntimeException e new RuntimeException(年龄不合理); throw e; }else{ this.age age; } } }上述代码运行结果如下图所示RuntimeExcepion与Exception的区别注意观察如下方法的源码Integer类public static int parseInt(String text)throws NumberFormatException此方法抛出了异常 但是使用时却不需要进行try…catch捕获处理原因因为NumberFormatException并不是Exception的直接子类而是RuntimeException的子类只要是RuntimeException的子类则表示程序在操作的时候可以不必使用try…catch进行处理如果有异常发生则由JVM进行处理。当然也可以通过try catch处理。自定义异常类编写一个类 继承Exception并重写一参构造方法 即可完成自定义受检异常类型。编写一个类 继承RuntimeException并重写一参构造方法 即可完成自定义运行时异常类型。例如class MyException extends Exception{ // 继承Exception表示一个自定义异常类 public MyException(String msg){ super(msg) ; // 调用Exception中有一个参数的构造 } } public class Demo{ public static void main(String[] args){ Person person new Person(); person.setAge(-1); } } class Person{ private int age; public void setAge(int age) throws MyException{ if(age0 || age180){ MyException e new MyException(年龄不合理); throw e;//此处会飘红因为属于受检异常所以必须得抛出异常;不要捕获异常因为自己生成异常又自己捕获异常很脑残 }else{ this.age age; } } }自定义异常可以做很多事情 例如class MyException extends RuntimeException{ public MyException(String msg){ super(msg) ; //在这里给维护人员发短信或邮件 告知程序出现了BUG。 } }try-with-resources//jdk1.7之前 public static void main(String[] args){ FileReader fr null; try{ fr new FileReader(d://book.txt); int c fr.read();//读取一个字节 System.out.prinyln((char)c); } catch(IOException e){ e.printStackTrace(); } finally { try{ fr.close(); } catch (Exception e){ e.printStackTrace(); } } } //jdk1.7时 public static void main(String[] args){ try(FileReader fr new FileReader(d://book.txt)){//try小括号里的对象必须是实现了AutoCloseable这样才会自动关闭对象 int c fr.read();//读取一个字节 System.out.prinyln((char)c); } catch(IOException e){ e.printStackTrace(); } } //jdk9进行了优化 public static void main(String[] args){ FileReader fr new FileReader(d://book.txt); PrintWriter pw new PrintWriter(d://book.txt); try(fr;pw){//try小括号里可以放置多个对象对象之间用分号分隔 int c fr.read();//读取一个字节 System.out.prinyln((char)c); } catch(IOException e){ e.printStackTrace(); } } //自定义实现了Closeable的对象 public static void main(String[] args){ CloseDemo d new CloseDemo(); try(d){ } catch(Exception e){ } } static class CloseDemo implements Closeable{ Override public void close() throws IOException{ Sytem.out.println(close方法被调用了); } }//输出close方法被调用了构造方法构造器Person p new Person();在右侧Person后面出现的小括号, 其实就是在调用构造方法作用用于对象初始化。执行时机在创建对象时,自动调用特点所有的Java类中都会至少存在一个构造方法如果一个类中没有明确的编写构造方法, 则编译器会自动生成一个无参的构造方法, 构造方法中没有任何的代码如果自行编写了任意一个构造器, 则编译器不会再自动生成无参的构造方法。定义的格式 :与普通方法基本相同, 区别在于: 方法名称必须与类名相同, 没有返回值类型的声明 建议自定义无参构造方法不要对编译器形成依赖避免错误发生。当类中有非常量成员变量时建议提供两个版本的构造方法一个是无参构造方法一个是全属性做参数的构造方法。当类中所有成员变量都是常量或者没有成员变量时建议不提供任何版本的构造。重载Overload方法的重载方法名称相同, 参数类型或参数长度不同或顺序不同, 可以完成方法的重载 方法的重载与返回值无关方法的重载 ,可以让我们在不同的需求下, 通过传递不同的参数调用方法来完成具体的功能。int sum(int x, int y){ int z x y; return z; } double sum(double x, double y){ double z x y; return z; }构造方法的重载一个类, 可以存在多个构造方法 参数列表的长度或类型不同即可完成构造方法的重载 构造方法的重载 ,可以让我们在不同的创建对象的需求下, 调用不同的方法来完成对象的初始化 重写Override参数列表必须完全与被重写的方法相同返回类型必须完全与被重写的返回类型相同访问权限不能比父类被重写的方法的访问权限更低。例如父类方法为public子类就不能为protected;父类的成员方法只能被它的子类继承声明为static和private的方法不能被重写但是能够被再次声明public class Demo{ public static void main(String[] args){ Student studentnew Student(); student.say(); } } class Person{ public void say(){ System.out.println(锄禾日当午汗滴禾下土。); } } class Student extends Person{ public void say(){ System.out.println(锄禾日当午玻璃好上霜。要不及时擦整不好得脏。); } } //结果为 //锄禾日当午玻璃好上霜。要不及时擦整不好得脏。重写与重载的区别重写方法名返回值相同参数相同重载方法名相同返回值相同参数可以不同个数可以不同重写发生在父子类中重载发生在一个类中重载与访问权限无关 异常处理重载与异常无关 重写异常范围可以更小但是不能抛出新的异常 Java两种核心机制Java 虚拟机(Java Virtual Machine) JVMJVM 可以理解成一个可运行 Java 字节码的虚拟计算机系统它有一个解释器组件可以实现 Java 字节码和计算机操作系统之间的通信对于不同的运行平台有不同 的 JVM。JVM 屏蔽了底层运行平台的差别实现了“一次编译随处运行”。垃圾回收器(Garbage Collection) GC不再使用的内存空间应当进行回收-垃圾回收。在 C/C等语言中由程序员负责回收无用内存。Java 语言消除了程序员回收无用内存空间的责任JVM 提供了一种系统线程跟踪存储空间的分配情况。并在 JVM 的空闲时检查并释放那些可以被释放的存储空间。垃圾回收器在 Java 程序运行过程中自动启用程序员无法精确控制和干预。JAVA跨平台原理标识符Java 对包、类、方法、参数和变量等要素命名时使用的字符序列称为标识符。规则如下:由字母、数字、下划线_和美元符号$组成。不能以数字开头。区分大小。长度无限制。不能是 Java 中的保留关键字。标识符命名习惯见名知意。关键字Java 中有一些赋予特定的含义有专门用途的字符串称为关键字keyword。全部是小写。this在Java基础中this关键字是一个最重要的概念。使用this关键字可以完成以下的操作调用类中的属性调用类中的方法或构造方法 注意在一个构造方法中调用另一个构造方法时调用的代码必须编写在构造方法的第一行。表示当前对象class Person{ private String name; private int age; Person(){ //调用下面的构造方法如果下面还有代码必须写在第一行 this(张三,12); } Person(String name,int age){ //调用类中的属性 this.namename; this.ageage; } }static概述static表示“静态”的意思可以用来修饰成员变量和成员方法(后续还会学习 静态代码块 和 静态内部类)。 static的主要作用在于创建独立于具体对象的域变量或者方法 。简单理解被static关键字修饰的方法或者变量不需要依赖于对象来进行访问只要类被加载了就可以通过类名去进行访问。 并且不会因为对象的多次创建 而在内存中建立多份数据 。重点 静态成员 在类加载时加载并初始化 无论一个类存在多少个对象 , 静态的属性, 永远在内存中只有一份( 可以理解为所有对象公用 ) 在访问时 静态不能访问非静态 , 非静态可以访问静态 静态修饰的方法被调用时有可能对象还未创建 //示例一 class Demo{ public static void main(String[] args){ /** Emp e1 new Emp(张三,北京); Emp e2 new Emp(李四,北京); Emp e3 new Emp(王二,北京); Emp e4 new Emp(麻子,北京); //假设公司迁址到天津 e1.setRegion(天津); e2.setRegion(天津); e3.setRegion(天津); e4.setRegion(天津); *///上述代码替换地址工作量非常大所以可以把地址定义成静态变量 Emp.region北京; Emp e1 new Emp(张三); Emp e2 new Emp(李四); Emp e3 new Emp(王二); Emp e4 new Emp(麻子); Emp.region天津; } } class Emp{ private String name; //private String region; static String region; Emp(String name,String region){ this.namename; this.regionregion; } Emp(String name){ this.namename; } Emp(){} public String getName(){ return name; } public void setName(String name){ this.namename; } public String getRegion(){ return region; } public void setRegion(String region){ this.regionregion; } } //示例二 public class Demo { public static void main(String[] args) { Clothes clothes1 new Clothes(); Clothes clothes2 new Clothes(); Clothes clothes3 new Clothes(); } } class Clothes{ static int count; Clothes(){ count; System.out.println(序号count); } } //输出 //序号1 //序号2 //序号3finalfinal用于修饰属性(类里定义的标识符称为属性)和变量(方法体里定义的标识符成为变量通过final修饰的属性和变量都是常量就是不能再次赋值的变量或属性 final修饰的局部变量只能赋值一次可以先声明后赋值final修饰的成员属性必须在声明时赋值 全局常量public static final可以在任何位置被访问 常量的命名规范由一个或多个单词组成单词之间必须使用下划线隔开所有字母大写例如SQL_INSERT //如果常量定义时没有赋值初始值那么可以赋值一次 final int a; a10;final用于修饰类final修饰的类不能被继承。final用于修饰方法final修饰的方法不能被子类重写。权限修饰符[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-971NLHBM-1613955209061)(java%E5%9F%BA%E6%9C%AC%E7%9F%A5%E8%AF%86.assets/image-20210121165006550.png)]封装private//我们观察如下代码 class Person{ private String name ; // 表示姓名 private int age ; // 表示年龄 void tell(){ System.out.println(姓名 name 年龄 age) ; } }; public class Demo{ public static void main(String args[]){ Person per new Person() ; per.name 张三 ; per.age -30 ; per.tell() ; } }; //以上的操作代码并没有出现了语法错误但是出现了逻辑错误 年龄-30岁 在开发中 为了避免出现逻辑错误 我们建议对所有属性进行封装并为其提供setter及getter方法进行设置和取得 操作。 修改代码如下 class Person{ private String name ; // 表示姓名 private int age ; // 表示年龄 void tell(){ System.out.println(姓名 getName() 年龄 getAge()) ; } public void setName(String str){ name str ; } public void setAge(int a){ if(a0a150) age a ; } public String getName(){ return name ; } public int getAge(){ return age ; } }; public class OODemo10{ public static void main(String args[]){ Person per new Person() ; per.setName(张三) ; per.setAge(-30) ; per.tell() ; } };代码块普通代码块在执行的流程中 出现的 代码块 我们称其为普通代码块。构造代码块在类中的成员代码块 我们称其为构造代码块 在每次对象创建时执行 执行在构造方法之前。静态代码块在类中使用static修饰的成员代码块 我们称其为静态代码块 在类加载时执行。 每次程序启动到关闭 只会 执行一次的代码块。同步代码块在后续多线程技术中学习。面试题构造方法 与 构造代码块 以及 静态代码块的执行顺序静态代码块 -- 构造代码块 -- 构造方法public static void main(String[] args){ //普通代码块就是{}的范围 { } Person p1 new Person(); Person p2 new Person(); } class Person{ //构造代码块 //区别于构造方法无论用户调用哪一个构造方法来创建对象构造代码块都必然执行。 { System.out.println(对象创建时执行1); } //静态代码块 //随着类的加载第一次使用静态代码执行。 //因为类只加载一次所以静态代码只执行一次。 static{ System.out.println(静态代码块执行) } //构造方法 //构造方法不一定会执行因为构造方法存在重载这就取决于用户创建对象时采用哪个重载。 public Person(){ System.out.println(对象创建时执行2); } } //输出 静态代码块执行 只执行一次 // 对象创建时执行1 // 对象创建时执行2 // 对象创建时执行1 // 对象创建时执行2包把功能相似或相关的类或接口组织在同一个包中方便类的查找和使用。包如同文件夹一样不同的包中的类的名字是可以相同的当同时调用两个不同包中相同类名的类时应该加上包名加以区别。因此包可以避免名字冲突。包也限定了访问权限拥有包访问权限的类才能访问某个包中的类。包的使用规则- 包中java文件的定义在.java文件的首部 必须编写类所属哪个包 格式package 包名;- 包的定义通常由多个单词组成 所有单词的字母小写 单词与单词之间使用.隔开 一般命名为“com.公司名.项目名.模块名…”。规范由来由于Java面向对象的特性每名Java开发人员都可以编写属于自己的Java Package为了保障每个Java Package命名的唯一性在最新的Java编程规范中要求开发人员在自己定义的包名前加上唯一的前缀。由于互联网上的域名称是不会重复的所以多数开发人员采用自己公司在互联网上的域名称作为自己程序包的唯一前缀。例如com.java.xxx根据最近的行业调研和招聘数据AI的发展确实对Java工程师提出了新挑战但也带来了明确的转型机遇。其现状可概括为市场分化明显初级岗位收缩但“JavaAI”的复合型人才需求正在崛起。️ 给Java工程师的转型行动建议转变核心角色定位目标应从“业务代码实现者”转向 “智能系统构建者” 或 “AI与业务的中枢架构师” 。这意味着你的核心价值在于设计能容纳AI能力的系统、确保其稳定高效运行并深刻理解业务以找到AI的最佳落地场景构建“Java AI”双技能栈巩固Java深度深入JVM性能调优、分布式系统设计这是你区别于纯AI算法工程师的基石。学习AI应用层技术不必从零开始研究算法。优先学习如何使用AI工具和框架例如Prompt Engineering提示词工程高效驱动大模型的关键技能。AI应用框架学习 LangChain4J、Spring AI 等掌握在Java中集成和调度AI模型的方法。向量数据库了解Milvus等这是构建AI语义搜索、推荐系统的基础。从“用AI辅助编程”开始实践立即在日常工作中使用GitHub Copilot等工具亲身体验其如何改变工作流。同时警惕过度依赖将节约出的时间用于更高层的设计和优化工作。选择垂直领域深耕将你的Java经验与某个行业如金融、医疗、工业物联网结合成为既懂行业业务又懂AI落地解决方案的专家这会形成强大的竞争壁垒。因此捕获AI掌握技术是关键让AI成为我们最便利的工具.一定要把现有的技术和大模型结合起来而不是抛弃你们现有技术掌握AI能力的Java工程师比纯Java岗要吃香的多。即使现在裁员、降薪、团队解散的比比皆是……但后续的趋势一定是AI应用落地大模型方向才是实现职业升级、提升薪资待遇的绝佳机遇如何学习AGI大模型作为一名热心肠的互联网老兵我决定把宝贵的AI知识分享给大家。 至于能学习到多少就看你的学习毅力和能力了 。我已将重要的AI大模型资料包括AI大模型入门学习思维导图、精品AI大模型学习书籍手册、视频教程、实战学习等录播视频免费分享出来。因篇幅有限仅展示部分资料需要点击下方链接即可前往获取2025最新版CSDN大礼包《AGI大模型学习资源包》免费分享**一、2025最新大模型学习路线一个明确的学习路线可以帮助新人了解从哪里开始按照什么顺序学习以及需要掌握哪些知识点。大模型领域涉及的知识点非常广泛没有明确的学习路线可能会导致新人感到迷茫不知道应该专注于哪些内容。我们把学习路线分成L1到L4四个阶段一步步带你从入门到进阶从理论到实战。L1级别:AI大模型时代的华丽登场L1阶段我们会去了解大模型的基础知识以及大模型在各个行业的应用和分析学习理解大模型的核心原理关键技术以及大模型应用场景通过理论原理结合多个项目实战从提示工程基础到提示工程进阶掌握Prompt提示工程。L2级别AI大模型RAG应用开发工程L2阶段是我们的AI大模型RAG应用开发工程我们会去学习RAG检索增强生成包括Naive RAG、Advanced-RAG以及RAG性能评估还有GraphRAG在内的多个RAG热门项目的分析。L3级别大模型Agent应用架构进阶实践L3阶段大模型Agent应用架构进阶实现我们会去学习LangChain、 LIamaIndex框架也会学习到AutoGPT、 MetaGPT等多Agent系统打造我们自己的Agent智能体同时还可以学习到包括Coze、Dify在内的可视化工具的使用。L4级别大模型微调与私有化部署L4阶段大模型的微调和私有化部署我们会更加深入的探讨Transformer架构学习大模型的微调技术利用DeepSpeed、Lamam Factory等工具快速进行模型微调并通过Ollama、vLLM等推理部署框架实现模型的快速部署。整个大模型学习路线L1主要是对大模型的理论基础、生态以及提示词他的一个学习掌握而L3 L4更多的是通过项目实战来掌握大模型的应用开发针对以上大模型的学习路线我们也整理了对应的学习视频教程和配套的学习资料。二、大模型经典PDF书籍书籍和学习文档资料是学习大模型过程中必不可少的我们精选了一系列深入探讨大模型技术的书籍和学习文档它们由领域内的顶尖专家撰写内容全面、深入、详尽为你学习大模型提供坚实的理论基础。书籍含电子版PDF三、大模型视频教程对于很多自学或者没有基础的同学来说书籍这些纯文字类的学习教材会觉得比较晦涩难以理解因此我们提供了丰富的大模型视频教程以动态、形象的方式展示技术概念帮助你更快、更轻松地掌握核心知识。四、大模型项目实战学以致用当你的理论知识积累到一定程度就需要通过项目实战在实际操作中检验和巩固你所学到的知识同时为你找工作和职业发展打下坚实的基础。五、大模型面试题面试不仅是技术的较量更需要充分的准备。在你已经掌握了大模型技术之后就需要开始准备面试我们将提供精心整理的大模型面试题库涵盖当前面试中可能遇到的各种技术问题让你在面试中游刃有余。因篇幅有限仅展示部分资料需要点击下方链接即可前往获取2025最新版CSDN大礼包《AGI大模型学习资源包》免费分享