Angular4+路由
路由的作用就是(导航):会加载与请求路由相关联的组件,并获取特定路由的相关数据,这允许我们通过控制不同的路由,获取不同的数据,从而渲染不同的页面;
几种常见的路由配置:
Angular路由器是一个可选的外部Angular NgModule ,叫RouterModule;
路由器里面包含多种服务(RouterModule),多种指令(RouterOutlet,RouterLink,RouterLinkActive),和一套配置(Routes);
import { RouterModule} from '@angular/router';
RouterModule.forRoot([
{
path: 'test',
component: TestComponent
} ]) <a routerLink="test">Test</a> <router-outlet></router-outlet>
详细解析:
路由定义包括下面部分:
Path:路由器会用它来匹配浏览器地址栏中的地址,如’test’;
Component:导航到此路由时,需要加载的组件;
注意,path不能以斜杠(/)开头。 路由器会为解析和构建最终的URL,这样当我们在应用的多个视图之间导航时,可以任意使用相对路径和绝对路径。
这里用到了RouterModule对象为我们提供的两个静态方法:forRoot()和forChild() 来配置路由信息;
forRoot()
方法提供了路由需要的路由服务提供商和指令,并基于当前浏览器 URL 初始化导航;用于在模块中定义主要的路由信息,通过调用该方法使得我们的主模块可以访问路由模块中定义的所有指令;
a
标签中的routerLink 指令绑定一个字符串,字符串是path路径中配置的字符串,它将告诉路由器,当用户点击这个链接时,应该导航到哪里;
当然routerLink还可以绑定一个数组,就是我们的带参路由,下面会具体介绍的:
<a [routerLink]="['/test', id]">test</a>
还可以在上面这样配置添加一个routerLinkActive指令:
<a routerLink="test" routerLinkActive="active">test</a>
.active{
color:red
}
当此路由被点击时,字体会变成红色;这也是routerLinkActive的作用,使我们知道哪个路由处于激活状态;
当然还可以添加上这个[routerLinkActiveOptions]="{exact: true}" 只有当路由完全一样时,才会将active类加载上去:
<a routerLink="dashboard" routerLinkActive="active" [routerLinkActiveOptions]="{exact: true}">Dashboard</a>
chrome控制台这样显示:
可见routerLink还是会自动将路由链接到href上的;class="active“也作用上去啦;
当切换路由时:
class="active” 移到我点击的路由上,只是应该是调用了:ngAfterContentInit(),ngOnChanges(),ngOnDestroy()
注意:
第一个路径片段可以以 / ,./ 或 ../ 开头:
如果以 / 开头,路由将从根路由开始查找
如果以 ./ 开头或没有使用 / ,则路由将从当前激活路由的子路由开始查找
如果以 ../ 开头,路由往上一级查找
eg:<a [routerLink]="['../test', id]">test</a>
当然这里我们也可以通过在component里控制写:
import {Router} from '@angular/router';
<a (click)="go()">Heroes</a>
constructor(private router: Router) {}
go() {
this.router.navigate(['heroes']);
}
这种效果也是一样的;这里就需要注入router服务:
router方法用于切换路由很有用,下面会具体来介绍router服务的;
路由出口:RouterOutlet是由RouterModule提供的指令之一。当我们在应用中导航时,路由器就把激活的组件显示在<router-outlet>
里面。不写<router-outlet></router-outlet>会导致组件内容不加载进来,从而不显示内容;
但是一个组件可以共用一个routeroutlet,所以app.component.ts里面配置了<router-outlet></router-outlet>就可以啦;
第二种写法:
RouterModule.forRoot([...]) 将[] 及中间的内容当成配置文件提取出去;
RouterModule.forRoot(routes),
routes是我们需要导入的配置文件参数名:
import { routes} from './app-routing.module';
app-routing.module:中我们可以这样写:
import { DashboardComponent } from './dashboard/dashboard.component';
import { HeroesComponent } from './hero/heroes.component';
import { HeroDetailComponent } from './detail/hero-detail.component';
export const routes = [
{ path: '', redirectTo: '/dashboard', pathMatch: 'full' },
{ path: 'dashboard', component: DashboardComponent },
{ path: 'detail/:id', component: HeroDetailComponent },
{ path: 'heroes', component: HeroesComponent },
{ path: '**', component:
DashboardComponent}
];
{ path: '', redirectTo: '/dashboard', pathMatch: 'full' },
表示重定向路由:需要一个pathMatch属性,告诉路由器是如何用URL去匹配路由的路径的,没有这个属性就会报错;
意思就是当路由URL等于’’时,会去加载DashboardComponent组件;所以你运行你的服务端口号:localhost:4200首先加载的就会是这个组件;
{ path: '**', component:DashboardComponent}
**
路径是一个通配符,表示除了以上几种path,任何的路径都会加载DashboardComponent组件,这个记得写在路由配置最后
当然这种方式我们还能这么写:
import { NgModule } from '@angular/core';
import { Routes,RouterModule} from '@angular/router';
import { DashboardComponent } from './dashboard/dashboard.component';
import { HeroesComponent } from './hero/heroes.component';
const routes = [
{ path: '', redirectTo: '/dashboard', pathMatch: 'full' },
{ path: 'dashboard', component: DashboardComponent },
{ path: 'heroes', component: HeroesComponent }
];
@NgModule({
imports: [ RouterModule.forChild(routes) ],
exports: [ RouterModule ]
})
export class AppRoutingModule {}
无declarations
!声明是关联模块的重点。
我们将AppRoutingModule抛出去,当做一个路由模块,
app.module.ts中引入:
import { AppRoutingModule} from './app-routing.module';
imports:中导入这个就可以啦
AppRoutingModule
这种用法和上面这种用法是一样的
还有一点:如何解决第二次刷新出现404问题:
[RouterModule.forRoot(routes,{useHash:true})]
配置后面这一点,通过添加一个#,防止刷新第二次出现404;
http://localhost:4201/#
RouterModule.forChild(routes)写在子模块里面,而且这边用的是forChild(),不是forRoot(),使用forRoot()也是不会错的,但是一般情况下:
根模块中使用forRoot(),子模块中使用forChild()
forChild()
只能用在特性模块中,这样的一点好处就是我们不必在主模块就配置好所有的路由信息,在需要的时候将他们导入主模块;
参数化路由
{ path: 'detail/:id', component: HeroDetailComponent },
配置参数化路由格式: :id 是一个占位符,当导航到HeroDetailCompnent组件上时,会被填入一个特定的id;
这里我们是这样绑定的:
<a *ngFor="let hero of heroes" [routerLink]="['/detail', hero.id]" class="col-1-4"></a>
eg:
http://localhost:4201/detail/11
这时的id等于11;
传参类型的id作用可以根据传入的id不同让HeroDetailComponent显示不同的内容;
但是怎么能让其显示不同的内容呢? 也就和我们这个id有关系,如何获取这个id 用在我们的组件里面呢?
通过注入ActivatedRoute服务,一站式获取路由信息;
import { ActivatedRoute} from '@angular/router';
constructor(
private route: ActivatedRoute,
) {}
接下来我们这样试试:
public params;
this.route.params.subscribe(
params => {
this.params = params;
console.log(this.params);
}
);
这样获取来的是一个对象:
直接取id就能获取到了;
既然是一站式获取,肯定不止这几个功能 后面会具体介绍它:
路由配置是也可以通过子路由来配置children:
{
path: 'heroes',
component: HeroesComponent,
children: [
{ path: 'heroTest', component: HeroTestComponent },
]
}
是这样配置的;此时HeroTestComponent组件的路由其实是:’heroes/heroTest’;
懒加载loadChildren:
{
path:'loadtest',
loadChildren:'./loadtest/loadtest.module#LoadtestModule'
}
路由是这样配置的:
1.这里注意几点:
import { LoadtestComponent } from './loadtest/loadtest.component';
组件不需要在app.module.ts引入
2. loadtest.module.ts 也不需要在app.module.ts中引入;而是通过loadchildren属性,在需要的时候告诉Angular路由依据loadchildren属性配置的路径去加载LoadtestModule模块,这就是模块懒加载功能;当用户需要的时候才回去加载,大大减少了应用启动时的资源加载大小;
3.loadChildren后面的字符串由三部分组成:
(1) 需要导入模块路劲的相对路径
(2) #分隔符
(3) 导出模块类的名称
4.还有一点也是也是重要的:
loadtestModule代码是这样的:里面要引入自己的路由;
import { NgModule } from '@angular/core';
import {CommonModule} from '@angular/common';
import { LoadtestComponent } from './loadtest.component';
import {RouterModule} from '@angular/Router';
import {route} from './loadtest-routing.module';
@NgModule({
imports:[
CommonModule,
RouterModule.forChild(route),
],
declarations:[
LoadtestComponent
]
})
export class LoadtestModule{}
在route路由里面记得这样配置这样一句才不会出错:
import { LoadtestComponent } from './loadtest.component';
export const route = [
{
path:'',
component: LoadtestComponent
},
]
path:’’,才能保证代码不出错;
懒加载的文件要注意:
app.module.ts中:
declarations: [
AppComponent,
DashboardComponent,
HeroDetailComponent,
HeroesComponent,
TestComponent,
],
这里面的文件,采用懒在家的模块是引用不到得,因为lazy加载文件有自己的ngModule ,如果要使用的组件是同一个,最好建立一个shareModule模块;
采用commonModule 将共享文件放进去,之后的Module里使用再加载进imports中;
Router服务:
1. class Router{
2. errorHandler:ErrorHandler
3. navigated: boolean
4. urlHandlingStrategy:UrlHandlingStrategy
5. routeReuseStrategy:RouteReuseStrategy
6. config:Routes
7. initialNavigation():void
8. setUpLocationChangeListener():void
9. get routerState():RouterState
10. get url(): string
11. get events():Observable<Event>
12. resetConfig(config:Routes):void
13. ngOnDestroy():void
14. dispose():void
15. createUrlTree(commands: any[], navigationExtras:NavigationExtras):UrlTree
16. navigateByUrl(url: string|UrlTree, extras:NavigationExtras):Promise<boolean>
17. navigate(commands: any[], extras:NavigationExtras):Promise<boolean>
18. serializeUrl(url:UrlTree): string
19. parseUrl(url: string):UrlTree
20. isActive(url: string|UrlTree, exact: boolean): boolean
21. }
这是Router API为我们提供的方法和属性;
看看几个常用的:
1.navigate() 该方法支持的参数类型和routerLink指令一样,所以他们的作用也是一样的:
this.router.navigate(['test', id]);
或者:
this.router.navigate(['test']);
调用该方法后页面会自动跳转到对应的路由地址;
this.router.navigate(['test'], { relativeTo: this.route});
我们可以设置一个参照路径,参照路径this.route从ActivatedRoute里面取;
配置这个可以让自己知道相对于什么位置导航,this.route就是相对于当前的路由进行导航,
假如当前url:localhost:4200/hero ,那么导航后的结果就是:localhost:4200/hero/test
2.我们注意到还有一个:navigateByUrl()
这个叫做绝对路由;
this.router.navigateByUrl('home');
可以帮助你快速的切换到某个路由下面,如果你当时的路由是这样的:
localhost:4200/hero/test 点击这个路由后就是:localhost:4200/home 我们一般用这个路由来回到首页; 和navigate()的区别还有点是:这个不是根据参数来确定路由地址的
3.config 会将页面所有的路由配置信息都显示:
看看路由树:
4.url 输出当前 的路由path
eg:http://localhost:4200/detail/11
url: /detail/11
5.每次导航前都会调用events方法;
RouterModule.forRoot(routes, {enableTracing: true })
通过在控制台配置enableTracing: true可以在控制台看到相关改变;
注意:enableTracing: true 只能在forRoot()里面添加;
具体的事件有:
chrome控制台:
注意:这些事件是以Observable
的形式提供的
ActivateRoute API :
interface ActivatedRoute {
snapshot: ActivatedRouteSnapshot
url: Observable<UrlSegment[]>
params: Observable<Params>
queryParams: Observable<Params>
fragment: Observable<string>
data: Observable<Data>
outlet: string
component: Type<any>|string|null
get routeConfig(): Route|null
get root(): ActivatedRoute
get parent(): ActivatedRoute|null
get firstChild(): ActivatedRoute|null
get children(): ActivatedRoute[]
get pathFromRoot(): ActivatedRoute[]
get paramMap(): Observable<ParamMap>
get queryParamMap(): Observable<ParamMap>
toString(): string
}
1.parmaMap
第一步:import { Router, ActivatedRoute, ParamMap } from '@angular/router';
第二步:import 'rxjs/add/operator/switchMap';导入switchMap操作符是因为我们稍后将会处理路由参数的可观察对象Observable ;会在以后的章节中介绍操作符的;
第三步:
constructor(
private heroService: HeroService,
private route: ActivatedRoute,
private router: Router, ) {}
假定事先写好了HeroService:
this.route.paramMap
.switchMap((params: ParamMap) => this.heroService.getHero(+params.get('id')))
.subscribe(hero => this.hero = hero );
} 我们这样操作,前面已经介绍过用parmas获取参数;
所以这样写也可以,用的是paramMap就引入paramMap,params就引入Params
this.route.params
.switchMap((params: Params) => this.heroService.getHero(+params['id']))
.subscribe(hero =>
this.hero = hero;
} );
由于参数是作为Observable
提供的,所以我们得用switchMap
操作符来根据名字取得id
参数,并告诉HeroService
来获取带有那个id
的英雄。
2/snapshot(快照)
route.snapshot
提供了路由参数的初始值。 我们可以通过它来直接访问参数,而不用订阅或者添加Observable的操作符
所以获取参数的id还可以这样:
<a *ngFor="let hero of heroes" [routerLink]="['/detail', hero.id]" class="col-1-4"></a>
this.params = this.route.snapshot.paramMap.get('id'); console.log(this.params);
所以上面的代码改成这样更好:
this.params = this.route.snapshot.paramMap.get('id');
console.log(this.params);
this.heroService.getHero(this.params)
.then(hero => this.hero = hero);
两种方法:params 和snapshot到底什么时候该用哪种呢?
- 需要直接访问参数,主要获取初始值,不用订阅的情况下用snapshot;
- 需要连续导航多次的用params;
总结 ,路由主要是用到了这些方面啦:
给路由添加一些新特性:
一..添加动画
1. 在app.module.ts中引入启用Angular动画必备的:
import { BrowserAnimationsModule } from '@angular/platform-browser/animations';
记得在imports中导入:
2.在app.component.ts同级下创建一个animation.ts文件,用来存放我们的动画效果;
import { animate, AnimationEntryMetadata, state, style, transition, trigger } from '@angular/core'; export const slideInDownAnimation: AnimationEntryMetadata =
trigger('routeAnimation', [
state('*',
style({
opacity: 1,
transform: 'translateX(0)'
})
),
transition(':enter', [
style({
opacity: 0,
transform: 'translateX(-100%)'
}),
animate('0.2s ease-in')
]),
transition(':leave', [
animate('0.5s ease-out', style({
opacity: 0,
transform: 'translateY(100%)'
}))
])
]);
假定我有以上代码,视图进场和出场;
1.构建动画需要的库; 2.导出了一个名叫slideInDownAnimation的常量,并把它设置为一个名,用于外部引入此ts文件; 3.叫routeAnimation的动画触发器。带动画的组件将会引用这个名字。用在外部html页面引用 4.指定了一个通配符状态 —— *,它匹配该路由组件存在时的任何动画状态。 5. 定义两个过渡效果,其中一个(:enter)在组件进入应用视图时让它从屏幕左侧缓动进入(ease-in),另一个(:leave)在组件离开应用视图时让它向下飞出。
3,如何使用动画;
1.在需要的组件中引入变量名为:slideInDownAnimation的文件animation.ts;
import {slideInDownAnimation} from '../animation';
2.组件中配置
templateUrl: 'hero-detail.component.html',
animations: [slideInDownAnimation]
3.html模板中这样引入:
<div *ngIf="hero" [@routeAnimation]="'active'">
@routeAnimation 动画触发器名
点击之后会自动加载动画的;
二.多重路由出口
一般情况下:我们使用一个路由出口就行啦,什么情况下会使用第二路由呢?
1.创建一个新组件ComposemessageComponent
2.路由配置:
{
path:'compose',
component:ComposemessageComponent,
outlet:'popup'
}
3.html页面这样配置:
<nav>
<a routerLink="dashboard" routerLinkActive="active" [routerLinkActiveOptions]="{exact: true}">Dashboard</a>
<a (click)="go()" >Heroes</a>
<a routerLink="test" routerLinkActive="active" [routerLinkActiveOptions]="{exact: true}">Test</a>
<a routerLink="loadtest" routerLinkActive="active">loadTest</a>
<a [routerLink]="[{ outlets: { popup: ['compose'] } }]">Contact</a>
</nav>
<router-outlet></router-outlet>
<router-outlet name="popup"></router-outlet>
这是我的页面所有的路由配置;
点击Contact 不会替换其他的组件信息,注意看Url:http://localhost:4200/dashboard(popup:compose)
点击Contact url地址没有变成http://localhost:4200/contact而是采用圆括号加载
圆括号包裹的部分是第二路由。
第二路由包括一个出口名称(
popup
)、一个冒号分隔符和第二路由的路径(compose
)
而是显示在下面,点击test也是一样:
Contact路由加载的组件不会被清除,一直显示在下面,状态一直被激活;
这里我们就能知道第二路由的用处:即使在应用中的不同页面之间切换,这个组件也应该始终保持打开状态,多重出口可以在同一时间根据不同的路由来显示不同的内容;
但是什么时候清除我们的第二路由呢?如果我页面不需要呢?
注意:
<a (click)="go()" >Heroes</a>
go() {
this.router.navigateByUrl('heroes');
}
当点击Heroes时,Contact路由加载的内容就不会被显示:
原因是这样的:
它使用Router.navigateNyUrl()
方法进行强制导航,所以路由清除啦;
还可以这样清除:
this.router.navigate([{ outlets: { popup: null }}]);
outlets属性的值是另一个对象,该对象用一些出口名称作为属性名。 唯一的命名出口是'popup'。但这里,'popup'的值是null。null不是一个路由,但却是一个合法的值。 把popup这个RouterOutlet设置为null会清除该出口,并且从当前URL中移除第二路由popup
3.路由守卫
按照上面所说:任何用户都能在任何时候导航到任何地方,这样就有问题,可能此用户并没有权限切换到此路由,可能用户未登陆不能切换,或者做一些友好提示之后再切换;
所以路由守卫就来了:
守卫返回一个值,以控制路由器的行为:
1.如果它返回true
,导航过程会继续
2.如果它返回false
,导航过程会终止,且用户会留在原地。
也就是你导航的路由是可以取消的,路由守卫还有一个好处就是回退功能时,可以防止用户无限回退,走出app;
路由守卫怎么做:
用CanActivate来处理导航到某路由的情况。
用CanActivateChild来处理导航到某子路由的情况。
用CanDeactivate来处理从当前路由离开的情况.
用Resolve在路由激活之前获取路由数据。
用CanLoad来处理异步导航到某特性模块的情况。
返回的值是一个Observable<boolean>
或Promise<boolean>
,路由器会等待这个可观察对象被解析为true
或false
。
在分层路由的每个级别上,我们都可以设置多个守卫。 路由器会先按照从最深的子路由由下往上检查的顺序来检查CanDeactivate()
和CanActivateChild()
守卫。 然后它会按照从上到下的顺序检查CanActivate()
守卫。 如果特性模块是异步加载的,在加载它之前还会检查CanLoad()
守卫。 如果任何一个守卫返回false
,其它尚未完成的守卫会被取消,这样整个导航就被取消了。
看看路由守卫怎么实现:
1.new 一个新项目activeComponent;
2.编写守卫服务:
import { Injectable } from '@angular/core';
import { CanActivate } from '@angular/router';
@Injectable()
export class LoadTestService implements CanActivate{
canActivate() {
console.log('AuthGuard#canActivate called');
return true;
}
}
3.路由中这样导入我们的守卫:
import { ActiveComponent } from './active/active.component';
import {LoadTestService} from './loadtest.service';
export const route = [
{
path:'',
component: LoadtestComponent,
canActivate:[LoadTestService],
children:[
{
path:'a',
component: ActiveComponent
}
]
},
]
这样我们的ActiveComponent就是受保护的;
当然这只是模拟;还有更多用法,以后来列举;
Angular4+路由的更多相关文章
- angular4路由设置笔记
场景说明:angular4开发的一个后台项目 一.可以使用angular-cli创建一个带路由的项目,ng new 项目名称 --routing 会多创建一个app-routing.module.ts ...
- angular4 路由重用策略 RouterReuseStrategy
单页面应用现在是主流,随之而来的缺点:页面间切换时不能保存状态 angular4出了一个RouteReuseStrategy路由重用策略可以让组件所有的state和渲染好的html存起来,然后在切回去 ...
- Angular4.0 探索子路由和懒加载 loadChildren
参考文章: Angular4路由快速入门 http://www.jianshu.com/p/e72c79c6968e Angular2文档学习的知识点摘要——Angular模块(NgModule)h ...
- Angular4.0学习笔记 从入门到实战打造在线竞拍网站学习笔记之二--路由
Angular4.0基础知识见上一篇博客 路由 简介 接下来学习路由的相关知识 本来是不准备写下去的,因为当时看视频学的时候感觉自己掌握的不错 ( 这是一个灰常不好的想法 ) ,过了一段时间才发现An ...
- Angular4.x通过路由守卫进行路由重定向,实现根据条件跳转到相应的页面
需求: 最近在做一个网上商城的项目,技术用的是Angular4.x.有一个很常见的需求是:用户在点击"我的"按钮时读取cookie,如果有数据,则跳转到个人信息页面,否则跳转到注册 ...
- Angular4中路由Router类的跳转navigate
最近一直在学习angular4,它确实比以前有了很大的变化和改进,好多地方也不是那么容易就能理解,好在官方的文档和例子是中文,对英文不太好的还是有很大帮助去学习. 官方地址:https://angul ...
- Angular4.0从入门到实战打造在线竞拍网站学习笔记之二--路由
Angular4.0基础知识之组件 Angular4.0基础知识之路由 Angular4.0依赖注入 Angular4.0数据绑定&管道 路由 简介 接下来学习路由的相关知识 本来是不准备写下 ...
- angular4.0 路由守卫详解
在企业应用中权限.复杂页多路由数据处理.进入与离开路由数据处理这些是非常常见的需求. 当希望用户离开一个正常编辑页时,要中断并提醒用户是否真的要离开时,如果在Angular中应该怎么做呢? 其实Ang ...
- angular4.0路由传递参数、获取参数最nice的写法
研究ng4的官网,终于找到了我想要的方法.我想要的结果是用'&'拼接参数传送,这样阅读上是最好的.否则很多'/'的拼接,容易混淆参数和组件名称.一般我们页面跳转传递参数都是这样的格式:http ...
随机推荐
- 进程管理工具Supervisor(二)Events
supervisor可以当做一个简单的进程启动.重启.控制工具使用,也可以作为一个进程监控框架使用,作为后者,需要使用supervisor的Events机制. Event Listeners supe ...
- Nginx中并发性能相关配置参数说明
worker_processes:开启worker进程的数目,通常可设置为CPU核心的倍数.在不清楚的情况下,可设置成一倍于CPU核心数或auto(Nginx将自动发现CPU核心数). worker_ ...
- windows 连接Linux
服务器:阿里云 ecs 从 Windows 环境远程登录 Linux 实例 远程登录软件的用法大同小异.本文档以 Putty 为例,介绍如何远程登录实例.Putty 操作简单.免费.免安装, 下载地址 ...
- offsetWidth相关js属性
js你真的了解offsetWidth吗 offsetWidth是什么? 答:它可以获取物体宽度的数值 那么就只是这样吗! html部分 <div id="div1"> ...
- cloudera-manager所有服务提示时钟偏差问题解决办法
今天新部署Cloudera Manager Hadoop(CDH)集群 发现祖国江山一片红,所有服务都报时钟偏差 1.查看各服务器,时钟是否正确 发现并无问题 2.查看CDH主机配置----主机时钟偏 ...
- C++雾中风景6:拷贝构造函数与赋值函数
在进行C++类编写的过程之中,通常会涉及到类的拷贝构造函数与类的赋值函数.初涉类编写的代码,对于两类函数的用法一直是挺让人困惑的内容.这篇文章我们会详细来梳理拷贝构造函数与赋值函数的区别. 1.调用了 ...
- hdu_1358Period(kmp找循环前缀)
题目在这儿 Period Time Limit: 2000/1000 MS (Java/Others) Memory Limit: 65536/32768 K (Java/Others)Tota ...
- Open-air shopping malls(二分半径,两元交面积)
http://acm.hdu.edu.cn/showproblem.php?pid=3264 Open-air shopping malls Time Limit: 2000/1000 MS (Jav ...
- list容器的C++代码实现
#include <iostream> using namespace std; template <class T> class mylist;//前置声明 templat ...
- io流读取文件
package test; import java.io.BufferedReader;import java.io.BufferedWriter;import java.io.File;import ...