IDEA是由jetbrains开发的Java开发工具,该公司旗下还有如下产品:
- WebStorm:用于开发JavaScript、HTML5、CSS3等前端。
- PyCharm:用于开发Python。
- PhpStorm:用于开发PHP。
- RubyMine:用于开发Ruby/Rails。
一、IDEA基本界面介绍
1.1HelloWorld的编写
- Create New Project:创建一个新的工程。
- Import Project:导入一个现有的工程。
- Open:打开一个已有工程。比如:可以打开 Eclipse 项目。
- Check out from Version Control:可以通过服务器上的项目地址 check out Github 上面项目或其他 Git 托管服务器上的项目。
点击创建即可:
在SRC文件夹下创建软件包:
在软件包中创建HelloWorld类,并编写:
package com.java.Demo;
public class HelloWorld {
public static void main(String[] args){
System.out.println("Hello World");
}
}
二、工程与模块管理
2.1IDEA项目结构
即,在使用IDEA编写代码时并不会将java文件直接编写在项目(project)下,而是将项目划分为不同的模块(module)以实现不同的功能,而不同的模块下又创建不同的软件包(package)存放实现功能的具体java代码。
层级关系:
project(工程)->module(模块)->package(包)->类
具体的有:
- 一个project可创建多个module
- 一个module可创建多个package
- 一个package可创建多个class
例:创建一个项目
可以在项目下新建module(一个项目至少有一个模块,所以创建一个项目的过程就是创建一个模块的过程),module可以选择自己的语言、构建系统、JDK,也可使用当前项目默认的配置。
2.2项目结构详解
以下内容以标准的maven目录作为参考。
maven项目基本结构:
2.2.1项目设置:项目
- 名称:项目的名称。
- SDK:当前项目使用的软件工具包(即为JDK)。
- 语言级别:用于指定项目所用语言的版本和特性,会影响IDEA的语法检查、自动补全等功能,确保能充分利用语言新特性,且避免使用不兼容的语法或功能。目前使用最多的是JDK8,若选择JDK8以前的版本,代码中有基于Lambda的语法将会报错。
- 编译器输出:源程序编译后形成字节码文件的存储位置。
注意,idea项目默认编译输出到out文件夹,而maven项目则默认输出到target文件夹,故而此处与普通的java项目不同,不存在out文件夹。target目录包含classes(源代码字节码文件夹)、test-classes(测试文件字节码文件夹)与generated-sources(存放 Maven 构建过程中自动生成的源代码文件,这些文件通常是由代码生成工具或插件自动生成的,而不是由开发者手动编写的)。
2.2.2项目设置:模块
1.源
- 源代码:当除项目的src目录外还有其他一些特别的目录需要作为可编译的目录,就需要对该目录进行标注,注意,只有被标记为源代码、测试的目录才可新建Java类和软件包。
- 测试:标注可编译的单元测试目录。
- 资源:存放源代码运行所用的资源文件,如配置文件、前端页面等,资源文件会被编译到输出目录下。
- 测试资源:存放测试单元代码运行所用的资源文件。
- 排除的:编译文件(字节码文件)的输出路径。
2.路径
- 继承项目编译输出路径:选择后,当前模块的编译输出路径就会继承
项目结构-项目-编译输出
所给的路径,此处勾选后即会将编译结果输出到out文件夹中。如下图,此时target不再包含classes(源代码字节码文件夹)与test-classes(测试代码字节码文件夹),而是以普通java项目的形式输出到out文件夹中。
- 使用模块编译输出目录:使用模块自己的编译输出目录存放字节码文件,此处包含classes与test-classes两种目录。
- 排除输出目录:
- javaDoc:
- 外部注释:
2.2.3项目设置:库
用来存放项目级依赖,即可将整个项目所用的依赖添加到此处管理,可灵活选择其所作用的模块。对于一个多模块的项目来讲,建议项目使用的所有Jar包都放在这里统一管理,模块要使用时直接按需选择即可,而不需要自己再单独添加。
2.2.4项目设置:Facet
能看到项目的每个模块使用的框架、语言等情况,并且还可以对它们进行配置。常用的如Spring、Hibernate框架,可以通过Facet的自动探测功能,自动将模块添加到Facet中进行管理。
2.2.5项目设置:工件
可用于实现使用IDEA进行打包的功能,流程如下:
获得清单文件MANIFEST.MF:
Manifest-Version: 1.0
Main-Class: org.example.Main
选择构建-构建工件
即可得到相应java包:
打开终端即可运行该jar包:
PS C:\Users\28591\IDEA\myProjects\demoProject1\out\artifacts\demoProject1_jar> java -jar demoProject1.jar
Hello and welcome!i = 1
i = 2
i = 3
i = 4
i = 5
2.2.6平台设置:SDK
项目结构->SDK
SDK(Software Development Kit,软件工具包),在此处保存的即为Java软件开发工具包(JDK):
注意:
- IntelliJ IDEA是JVM平台IDEA,不仅仅支持Java还有其它语言如Kotlin,所以写成SDK而非JDK。
- SDK并不能决定语言等级,如使用JDK11,但仍可将语言等级调为8来编译运行。
2.2.7平台设置:全局库
2.3常见项目结构
2.4.1Web项目结构
三、断点调试
编好的程序在执行过程中发生错误时,可以借助程序调试来查找错误,Debug的调试步骤如下:
- 添加断点
- 启动调试
- 单步执行
- 观察变量和执行流程,找到并解决问题
debug的启动方式为:
四种断点类型:
3.1debug操作界面简介
- 显示执行点:点击后光标将跳转到当前正在执行(或者说是将要执行)的代码行。
- 步过:继续向下执行代码行,不会进入方法内部。对于有输出语句的代码,可在
控制台
(线程与变量
的右边)中查看。 - 步入:继续向下执行代码行,若会进入自己定义的方法、构造器等的内部(非自行定义就不会进入)。
- 强制步入:继续向下执行代码行,若代码行中有方法、构造器等,无论是自己定义还是jar包当中,都会进入内部。
- 智能步入:当前行有多个方法同时被执行,IDEA 将会询问你要进入哪个方法。
- 步出:从步入的方法内执行完该方法,然后退出到方法调用处。
- 单步跳出代码块:运行完当前代码块所有代码并退出。
- 运行到光标处:运行到当前光标所在的位置,会遵循断点和其他调试停止点的规则,如果在执行过程中遇到了断点,程序会在那里暂停执行。
- 强制运行到光标:运行到当前光标所在的位置,而不管是否有断点或者其他调试停止点。
- 对表达式求值:在调试的时可以查看某个变量的值,也可以计算某个表达式的值,甚至还可以写一串代码并求值,分别对应两种不同的模式。
模式一(表达式模式):可用于从数据库读取数据后对变量数据进行查看等场景。
模式二(代码片段模式):只能写代码片段,不能自定义方法。
- 恢复程序运行:恢复程序的运行,直到运行到下一个断点处停止运行。
- 重新运行:重新进行debug。
- 停止:退出debug模式。
- 暂停程序:暂停程序的运行,并启用debug模式。一般恢复程序运行后使用,会立即停止运行并debug(程序运行很快的话,很难有机会使用)。
- 查看断点:查看当前设置的所有断点及其信息。
- 忽略断点:所有断点图标变为白色(若在debug前使用,则会使debug模式失效,改为程序的正常执行),使其不再起作用,可在断点处右键,使其再次生效。
示例:
package org.example;
public class debug01 {
public static void main(String[] args) {
int[] arr1=new int[]{1,2,3,4,5};
System.out.println(arr1);
char[] arr2=new char[]{'a','b','c'};
System.out.println(arr2);
}
}
在断点一处强制步入得到函数:
public void println(Object x) {
String s = String.valueOf(x);
if (getClass() == PrintStream.class) {
// need to apply String.valueOf again since first invocation
// might return null
writeln(String.valueOf(s));
} else {
synchronized (this) {
print(s);
newLine();
}
}
}
在断点二处强制步入得到函数:
public void println(char[] x) {
if (getClass() == PrintStream.class) {
writeln(x);
} else {
synchronized (this) {
print(x);
newLine();
}
}
}
可见,虽然代码中都是使用了System.out.println()
函数,但在实际执行时发生了函数重载。
3.2行断点
可作用在任何一行代码上,图标为红色圆形。当程序执行到设置断点的行时将会被触发。
package org.example;
public class debug01 {
public static void main(String[] args) {
//1.
int m=10;
int n=20;
System.out.println("m="+m+",n="+n);
swap(m,n);
System.out.println("m="+m+",n="+n);
}
public static void swap(int m, int n) {
int temp=m;
m=n;
n=temp;
}
}
由于Java是值传递方式,故而swap()无法正确交换m与n的值,运行结果如下:
m=10,n=20
m=10,n=20
给int n=20;
、swap(m,n);
打上断点,并对程序进行调试(直接运行时断点并不会发生作用):
注意,此处的m与n是swap函数栈帧当中的变量(临时变量),但main函数栈帧当中的m与n并未发生改变,可点击main函数栈帧进行查看:
Java是一种值传递语言。
行断点可设置出发的相关条件,如,在for循环中i=6时使用行断点,就可进行设置:
注意:
- 条件语句的返回值应为布尔值(相当于嵌入了if判断语句)。
- 断点不应打在for语句上,否则程序第一次中断时for循环尚未执行,此时并不存在局部变量i。
3.3方法断点
方法断点的断点设置在方法签名上,默认当进入时断点可以被唤醒(而无需找出所有方法调用语句依次打上断点),也可以设置在方法退出时,断点也会被唤醒。多态的场景下,在父类或接口的方法上打断点,会自动调入到子类或实现类的方法。
程序代码:
package org.example;
public class debug01 {
public static void main(String[] args) {
}
}
class Father{
public void test(){
System.out.println("Father:test1");
System.out.println("Father:test2");
}
}
class Son extends Father{
@Override
public void test(){
System.out.println("Son:test1");
System.out.println("Son:test2");
}
}
interface Consumer{
void accept(String str);
}
class ConsumerImpl implements Consumer{
@Override
public void accept(String str) {
System.out.println("ConsumerImpl"+str);
}
}
案例一:在方法声明上打断点
public class debug01 {
public static void main(String[] args) {
Son instance=new Son();
instance.test();
}
}
将方法断点打在Son类中test函数的声明上,进行调试:
同样可以为方法断点设置条件:
在勾选上方法退出
时,test()函数在执行完退出时会自动打上断点并停留:
案例二:在父类上打断点(类的多态性)
public class debug01 {
public static void main(String[] args) {
Father instance = new Son();
instance.test();
}
}
将方法断点打在Father类中test函数的声明上,进行调试:
最终运行时的断点发生在了Son子类的test()方法上,通过此方法,在调试源码时可以直接跳转到子类的重载方法上,而不需要一个个的去找。
案例三:在接口上打断点(接口的多态性)
public class debug01 {
public static void main(String[] args) {
Consumer con=new ConsumerImpl();
con.accept("atguigu");
}
}
将方法断点打在Consumer接口中accept函数的声明上,进行调试:
最终运行时的断点发生在了实现类ConsumerImp的test()方法上,通过此方法,在调试源码时可以直接跳转到实现类的实现方法上,而不需要一个个的去找。
3.4属性断点
在类的属性上打断点,默认对属性的修改操作进行监控,定义Person类如下:
class Person{
private int id=1;
private String name;
private int age;
public Person(){}
{
id=2;
}
public Person(int id){this.id=id;}
public Person(int id, String name, int age) {
this.id = id;
this.name = name;
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 getAge() {return age;}
public void setAge(int age) {this.age = age;}
@Override
public String toString() {return "Person [id=" + id + ", name=" + name + ", age=" + age + "]";}
}
在debug之后,程序会在private int id=1;
停下,这是因为int变量初值为0,此处已是对id的值进行了修改。会依次在以下语句停下:
private int id=1;
id=2;
this.id=id;
当选上字段
访问后,会在toString()函数停下。
3.5异常断点
当程序抛出相对应的异常时将会触发,程序会定位到抛出异常的语句并自动停止执行。
package org.example;
public class debug01 {
public static void main(String[] args) {
int m=10;
int n=0;
int result=m/n;
System.out.println(result);
}
}
//抛出异常:java.lang.ArithmeticException
此时再进行调试就会在抛出异常断点处停止运行:
示例:
package org.example;
public class debug01 {
public static void main(String[] args) {
Person person = new Person();
System.out.println(person.getName().toUpperCase());
}
}
class Person{
String name;
int age;
public Person(){}
public Person(String name, int age) {}
public String getName() { return this.name; }
public int getAge() { return this.age; }
}
//报错:Exception in thread "main" java.lang.NullPointerException: Cannot invoke "String.toUpperCase()" because the return value of "org.example.Person.getName()" is null at org.example.debug01.main(debug01.java:6)
为java.lang.NullPointerException
添加异常断点,此时可清楚看到变量person的属性为null:
3.6线程调试
上述操作均是在主线程当中进行,且在调试时一步一步往下运行,不能发起其他请求,这是因为IDEA在Debug时默认阻塞级别是ALL,会阻塞其它线程,只有在当前调试线程走完时才会走其它线程。事实上,在设置断点时可设置断点的作用范围为当前线程,就不会影响其他线程的执行。
package org.example;
public class debug01 {
public static void main(String[] args) {
test("Thread1");
test("Thread2");
}
public static void test(String threadName) {
new Thread(
()->{
for(int i=0;i<100;i++) {
System.out.println(Thread.currentThread().getName()+":"+i);
}
},threadName
).start();
}
}
条件语句为:
"Thead2".equals(Thread.currentThread().getName())
此时只有线程2被阻断运行,线程1正常执行,即断点只对线程2起作用。
3.7强制结束
package org.example;
public class debug01 {
public static void main(String[] args) {
System.out.println("获取请求的数据");
System.out.println("调用写入数据库的方法");
insert();
System.out.println("程序结束");
}
private static void insert() {
System.out.println("进入insert()方法");
System.out.println("获取数据库连接");
System.out.println("将数据写入数据表中");
System.out.println("写出操作完成");
System.out.println("断开连接");
}
}
在常见的数据库项目中,若调试到System.out.println("获取数据库连接");
时已发现错误,不想insert()函数执行完(防止数据错误插入到数据表中),可通过强制返回按钮中断insert()函数的执行。
控制台输出为:
获取请求的数据
调用写入数据库的方法
进入insert()方法
获取数据库连接
程序结束
3.8自定义调试视图
package org.example;
import java.util.HashMap;
public class debug01 {
public static void main(String[] args) {
HashMap<Integer, String> map = new HashMap<>();
map.put(1,"高铁");
map.put(2,"网购");
map.put(3,"支付宝");
map.put(4,"共享单车");
System.out.println(map);
}
}
将断点打在map.put(1,"高铁");
处,在执行完后只能看到如下变量情况:
&emps;右键进入自定义数据视图
:
此时即可看到map中tabel数组的情况:
四、IDEA基本操作
4.1项目:创建空项目
可通过关闭项目
来关闭。
4.2模块的基本操作
1.创建module
以上操作可用于在项目(project)当中创建模块(module),事实上,创建项目的过程就是为该项目创建第一个module的过程,故此处module的创建不再赘述。
2.模块导入操作
此处要将左侧demoProject1项目下的org软件包导入到右侧的demo项目的当中,首先在资源管理器中打开,并将org软件包复制并粘贴到demo文件夹下,此时org软件包只显示为普通文件夹:
&emps;在项目结构中导入该模块:
最终得到:
此时org也成为了demo项目下的一个模块。事实上,也可直接在demoProject1项目中复制org文件夹,然后在demo文件夹上粘贴,再在项目结构中修改即可:
3.移除module
对module01模块右键点击移除模块后,module01对应的文件夹不会被删除,但不再属于Demo项目(项目结构当中也不能再查找到),并且只显示为是一个普通文件夹:
4.3软件包的基本操作
1.package的创建
源代码一般放在src文件夹中,故软件包(package)一般也在src目录下建立。
2.软件包的移除
与移除模块不同,此时会删除软件包所对应的文件夹。
3.package取名规范
- 公司域名倒着写:如尚硅谷域名为www.atguigu.com,则写代码时创建软件包结构为com.atguigu。
4.4IDEA基本设置说明
3.2解决引入java文件时的乱码问题
在idea以外平台编写时,编码方式可能并不是UTF-8,以上图为例,编码方式为ANSI(Windows系统中对应为GBK,cmd属性当中可见),导致将文件复制到idea项目下时会出现中文乱码,解决方式为:
选中需要更改编码方式的文件,对其特殊化处理即可:
2.4.3详细设置
如何打开详细设置